【问题标题】:recursive function that prints something打印某些东西的递归函数
【发布时间】:2021-03-10 10:41:42
【问题描述】:

我有一个[Int] 类型的列表,它只能包含两个值:01

例如:[0, 0, 1, 1, 0, 0]

我想遍历这个列表,当当前元素为 0 时,打印 '_',如果是 1,则打印 '*'

对于上一个列表,这是标准输出上的结果:

__**__

我认为我必须这样做:

displayLine :: [Int] -> IO [Int]
displayLine (0 : xs) =
    do
        print '_'
        -- next char
displayLine (1 : xs) =
    do
        print '*'
        -- next char

如何显示一个字符并继续递归下一个字符?

【问题讨论】:

  • 你可以……正常打电话给displayLine,不是吗?但更好的方法是分两部分编写:首先编写[Int] -> String 类型的函数来生成要打印的字符串,然后print 生成String

标签: haskell


【解决方案1】:

你用列表的尾部调用displayLine。但是这里的类型将是IO (),而不是IO [Int],因为该操作不会返回任何内容:

displayLine :: [Int] -> IO ()
displayLine (0 : xs) = do
        print '_'
        displayLine xs
displayLine (1 : xs) = do
        print '*'
        displayLine xs

您可能还想使用putChar :: Char -> IO () 打印单个字符,因为print 将调用show 并因此打印Char literal 和一个新行。 p>

然而,这重复了逻辑。我们可以利用mapM_ :: (Foldable f, Monad m) => (a -> m b) -> f a -> m () 来执行映射,其中每个元素执行一个单子动作:

displayLine :: [Int] -> IO ()
displayLine = mapM_ (putChar . f)
    where f 0 = '_'
          f 1 = '*'

例如:

Prelude> displayLine [0,1,1,0,1,0] >> putStrLn ""
_**_*_

末尾的putStrLn "" 用于在字符打印后开始新行。

【讨论】:

    【解决方案2】:

    do-notation 的好处在于,代码通常看起来就像在命令式语言 (only better) 中的样子:几个语句和最后一个 return

    displayLine (0 : xs) =  do
        print '_'
        ys <- displayLine xs -- ys will be identical to xs,
        return (0:ys)
    

    不要忘记为displayLine [] 添加一个案例,除非您的所有列表都是无限的......

    顺便说一句,你真的需要return 任何东西,因为列表返回不变?如果不是,displayLine 的类型可以是[Int] -&gt; IO (),定义可以更简单..

    【讨论】:

      猜你喜欢
      • 2016-09-25
      • 1970-01-01
      • 2012-01-25
      • 1970-01-01
      • 1970-01-01
      • 2021-12-05
      • 2011-07-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多