【问题标题】:How can I implement this kind of IO action?我怎样才能实现这种 IO 动作?
【发布时间】:2021-01-03 23:19:46
【问题描述】:

我有以下数据类型,它编码任意实数:

newtype ArbReal = ArbReal {approximate :: Word -> Integer}

我有以下函数,它找到实数收敛序列的极限:

inexactToReal :: (Word -> ArbReal) -> ArbReal
inexactToReal f = ArbReal $ \n -> let
    ArbReal g = f n
    in g n

我实现了一个常数,十的自然对数:

ln10 :: ArbReal
ln10 = inexactToReal $ \n -> if 0 == n
    then 2
    else let
        halfN = ceiling (n % 2)
        a = inexactToReal $ \m -> go (2 * logBaseZ m 2) 1 (4 / 10 ^ halfN)
        in pi / (2 * a * fromInteger halfN)
  where
    go n a b = let
        newA = (a + b) / 2
        in if 0 == n
            then newA
            else go (n-1) newA (sqrt (a * b))

但这有一些错误。我认为这陷入了无限循环。所以我想把它变成一个调试程序。这就是问题所在。考虑上面go 的以下变体:

debugAGM :: Word -> ArbReal -> ArbReal -> IO ArbReal
debugAGM n a b = let
    newA = (a + b) / 2
    in if 0 == n
        then putStrLn "Iteration complete." >> return newA
        else do
            putStrLn ("debugAGM iteration: n = " ++ show n)
            debugAGM (n-1) newA (sqrt (a * b))

IO 太多了。没有inexactToReal 的变体可以处理这个问题。

考虑一下:

debugAGM :: Word -> ArbReal -> ArbReal -> (ArbReal, IO ())
debugAGM n a b = let
    newA = (a + b) / 2
    in if 0 == n
        then (newA, putStrLn "Iteration complete.")
        else let
            (x, action) = debugAGM (n-1) newA (sqrt (a * b))
            in (x, putStrLn ("debugAGM iteration: n = " ++ show n) >> action)

IO 太少了。 IO 操作与返回值并行。没有inexactToReal 的变体可以处理这个问题。

总之,我必须以某种方式转义IO,以便实现以下目标:

  • 返回的值,本质上是一个函数,必须是纯的。

  • 但还必须有一个 IO 操作,该操作取决于返回函数的潜在参数。

如何做到这一点?命令式语言可以轻松做到这一点,真是令人心碎。

【问题讨论】:

  • 看看Debug.Trace,它对于调试此类问题非常有用。 Audrey Tang 将Debug.Trace 描述为“参照透明绿洲中的清新沙漠”
  • 我不太懂inexactToReal的实现。如果你去掉新类型,并用类型参数替换 Word 和 Integer,它是inexactToReal :: (a -> a -> b) -> a -> b; inexactToReal f x = f x x。这与找到序列的极限有何对应?
  • 另外,ln10 如何进行类型检查?它似乎经常将 ArbReal 与数字类型混合在一起,但没有声明 Num 实例。
  • @amalloy 我实现了instance Num ArbReal,但这涉及到太多的野蛮数学,无法在此处展示。
  • @amalloy 构造函数ArbReal 仅标记具有渐近固定收敛速度的柯西序列,因此inexactToReal 有效地计算了一个极限。

标签: debugging haskell io monads io-monad


【解决方案1】:

我认为这可行:

debugAGM :: Word -> ArbReal -> ArbReal -> Word -> (Integer, IO ())
debugAGM n a b p = let
    newA@(ArbReal f) = (a + b) / 2
    in if 0 == n
        then f p `seq` (f p, putStrLn "Iteration complete.")
        else let
            (x, action) = debugAGM (n-1) newA (sqrt (a * b)) p
            in x `seq` (x, putStrLn ("debugAGM iteration: n = " ++ show n) >> action)

ln10 :: Word -> IO ()
ln10 n = if 0 == n
    then putStrLn "2"
    else let
        halfN = ceiling (n % 2)
        f = \p -> debugAGM (2 * logBaseZ p 2) 1 (4 / 10 ^ halfN) p
        ArbReal g = pi / (2 * ArbReal (fst . f) * fromInteger halfN)
        in snd (f n) >> putStrLn (show (g n))

【讨论】:

  • 那些seq 电话绝对不会做任何事情。评估 IO 与执行 IO 不同。
猜你喜欢
  • 2021-01-20
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-01
  • 2020-02-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多