foo:: Int -> Int -> Int
foo z x = if (z < 100)
then z * foo (z+(x*z)) z
else z
每次从它自己调用时,你会如何打印出(整数z)一个输出?你能有返回IO和Int的函数吗?你需要辅助功能吗?
答案 0 :(得分:19)
为简单起见,您可以使用trace。然而,它不受欢迎的真实生产代码,因为它打破了参考透明度。
trace
需要String
打印并返回值。
import Debug.Trace
foo:: Int -> Int -> Int
foo z x = trace ("z = " ++ show z) $ if (z < 100)
then z * foo (z+(x*z)) z
else z
*Main> foo 1 2
z = 1
z = 3
z = 6
z = 24
z = 168
72576
答案 1 :(得分:10)
为了完整起见,我将回答这个问题:
你能有返回IO和Int的函数吗?
......非常字面。答案是“是的!”......它有时甚至是有用的。可能这不是你想要做的初学者,但如果是,这是一个样本。
foo :: Int -> Int -> (IO (), Int)
foo z x = if z < 100 then (print z >> io, z * rec) else (return (), z) where
(io, rec) = foo (z+x*z) z
例如,您可以通过设置
来打印递归调用main = fst $ foo 13 7
或者您可以通过设置
来打印答案main = print . snd $ foo 13 7
或其他六件事。当然,IO ()
类型有点难以检查;你可能会考虑写这样的东西:
foo' :: Int -> Int -> Writer [Int] Int
foo' z x = if z < 100
then tell [z] >> fmap (z*) (foo' (z+x*z) z)
else return z
使用它与上面非常类似,但引入了额外的runWriter
;例如,你可以写下这两个中的任何一个:
main = print . snd . runWriter $ foo' 13 7 -- to print a list of the calling values
main = print . fst . runWriter $ foo' 13 7 -- to print the result
这种方法的优点是你可以获得一个调用值列表,而不是打印该列表的IO操作,这样你就可以用更有趣的方式来调用这些调用。
答案 2 :(得分:4)
执行I / O的任何函数都必须在IO
monad:
foo :: Int -> Int -> IO Int
foo z x = print z >> if z < 100 then fmap (z*) (foo (z + x * z) z) else return z
请注意,if
表达式的两个分支现在也必须位于IO
。
这与返回&{34; IO
和Int
&#34;不同。 IO Int
是表示I / O操作的值的类型,在执行时,将产生Int
作为其结果(可能在执行一些I / O之后)。因此,foo
的上述定义需要Int
和Int
,并返回最终会产生Int
的I / O操作。
答案 3 :(得分:3)
在@ is7s的答案的基础上,使用Debug.Trace
的一个有用的习惯是这样做:
import Debug.Trace
foo:: Int -> Int -> Int
foo z x | trace ("z = " ++ show z) False = undefined
foo z x = if (z < 100)
then z * foo (z+(x*z)) z
else z
在这里,我们在保护中引入了foo
的{{1}}定义,其中trace
的评估结果为False
,因此它将始终落在原始定义中。通过这种方式,我们不会扰乱我们的功能,并且可以通过注释掉线来打开或关闭跟踪。