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

前奏> find_primes [ ] 2

4

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
于 2011-08-27T11:12:52.787 回答
2

rkhayrov在他们的回答中没有涵盖更多选项

如果您只需要调试信息,请考虑使用Debug.Trace's tracefunction

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类型类。

于 2011-08-27T16:56:33.660 回答