【问题标题】:Haskell Print while in recursion?递归时Haskell Print?
【发布时间】:2012-04-08 13:58:22
【问题描述】:
foo:: Int -> Int -> Int
foo z x = if (z < 100) 
             then z * foo (z+(x*z)) z
             else z

每次从自身调用时如何打印输出(整数 z)?你能有返回 IO 和 Int 的函数吗?您需要辅助功能吗?

【问题讨论】:

    标签: haskell recursion


    【解决方案1】:

    任何执行 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 中。

    这与返回“IOInt”不同。 IO Int 是表示 I/O 操作的值的类型,当执行该操作时,将产生 Int 作为其结果(可能在执行一些 I/O 之后)。所以上面对foo 的定义接受了一个Int 和一个Int 并返回一个I/O 操作,该操作最终将导致Int

    【讨论】:

      【解决方案2】:

      为简单起见,您可以使用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
      

      【讨论】:

        【解决方案3】:

        为了完整起见,我将回答这个问题:

        你能有返回 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 操作,因此您可以以许多更有趣的方式处理调用。

        【讨论】:

        • +1 我们的 Haskell 新手应该比我们通常教他们更快地了解 Writer 之类的东西。
        • @DanBurton,如果我们更早介绍 X,我们必须稍后介绍 Y。在这种情况下 Y 是什么?
        • @luqui 恕我直言列表理解学得太早了。确实,它们相当容易理解,但学生最好学习实际的 monad 接口,然后再学习如何理解语法对它进行脱糖。我敢说,你自己的类型类和你自己的实例也应该稍后再写,尽管我没有过多考虑这样做的后果。
        • @luqui :我猜 Debug.Trace.trace 然后
        • 您能否评论将Writer 与列表一起使用而不是DList 对性能的影响?
        【解决方案4】:

        基于@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
        

        在这里,我们在评估为False 的守卫中引入了footrace 的定义,因此它将始终落入原始定义。这样,我们就不会干扰我们的函数,并且可以通过注释掉该行来打开或关闭跟踪。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-07-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-09-11
          • 2011-02-14
          • 2019-02-28
          相关资源
          最近更新 更多