Генерация вывода в работающей программе на haskell - PullRequest
4 голосов
/ 27 августа 2011

Исходя из (SWI) Пролога Мне очень трудно заставить 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 голосов
/ 27 августа 2011

У вас есть три варианта:

Первый: распространить повсюду 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).Полезно для стрельбы в ноги и в целях отладки.

В-третьих: вместо того, чтобы смешивать ввод / вывод и вычисления в коде, лениво генерировать [потенциально бесконечную] структуру данных, содержащую промежуточные результаты в чистой функции, и использовать еепо отдельности.Например, бесконечный список факториалов можно записать в виде:

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

и использовать следующим образом, чтобы получить тот же результат, что и при вызове fac 10 в приведенном выше примере:

forM_ (take 11 $ zip [0..] facs) $ \(i, x) ->
    putStrLn $ show i ++ "! = " ++ show x
2 голосов
/ 27 августа 2011

Есть еще пара вариантов, которые Рхайров не охватил в своем ответе

Если вы просто хотите отладочную информацию, рассмотрите возможность использования функции Debug.Trace * trace .

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.

...