在运行的 Haskell 程序中生成输出

4

作为来自(SWI) Prolog的人,我发现让Haskell实时输出非常困难。

最简单的例子,我希望Haskell在每次迭代时打印一些东西:

fac 0 = 1  
fac n = fac ( n-1 ) * n

或者我希望能够从一个永远不会停止的程序中获取输出...

-- A possible halt statement...  
-- find_primes l 100 = l  
find_primes l n = if ( is_prime l n ) then find_primes nn s else find_primes l s  
where   s = n + 1
nn = n:l

is_prime :: Integral a => [a] -> a -> Bool  
is_prime [] n = True  --print the prime number on the fly    
is_prime (h:t) n = if ( r /= 0 ) then is_prime t n else False  
where r =  n mod h

Prelude> find_primes [ ] 2

2个回答

13

你有三个选项:

第一:在代码中广泛使用 IO,并像高级的命令式语言一样编写 Haskell 代码。

fac 0 = putStrLn "0! = 1" >> return 1
fac n = do
    x <- fac (n - 1)
    let r = x * n
    putStrLn $ show n ++ "! = " ++ show r
    return r

第二步:使用unsafePerformIO及其衍生物(例如Debug.Trace)。它对于自我毁灭和调试目的非常有用。

第三步:不要在代码中混合I/O和计算,而是在纯函数中惰性地生成一个[可能无限的]包含中间结果的数据结构,并单独消耗它。例如,无限阶乘列表可以编写为:

facs = scanl (*) 1 [1..]

按照以下方式使用,即可产生与上面示例中调用 fac 10 相同的结果:

forM_ (take 11 $ zip [0..] facs) $ \(i, x) ->
    putStrLn $ show i ++ "! = " ++ show x

8
这些选项中,第三个最好。 - dave4420

2

还有几个选项是rkhayrov在回答中没有提到的(链接1)

如果您只需要调试信息,请考虑使用{{link2:Debug.Tracetrace 函数}}。

import Debug.Trace

fact :: (Ord a, Num a) => a -> a
fact n | n > 1 = traceShow n $ n * fact (n-1)
       | otherwise = traceShow n $ 1

我不能推荐将其用于除了调试之外的其他用途。它绝对不应该用于生产代码。
如果您想在工作时记录信息,请考虑使用 Writer Monad
import Control.Monad.Writer.Lazy
import Control.Applicative ((<$>))

fact :: (Ord a, Num a) => a -> Writer [String] a
fact n = do
  tell $ [show n]
  if n > 1
    then
      (n*) <$> fact (n-1)
    else
      return 1

你也可以更加通用,使用 MonadWriter 类型类。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接