Haskell 递减函数

Haskell decrement function

我正在尝试学习 Haskell,这是我在函数式编程方面的第一个方法。我在创建一个将数字作为输入并递归地逐一打印该数字直到 0 的函数时遇到了一些问题。

printDescending n = if n > 0
                        then printDescending n - 1 return n
                        else return n - 1

我希望能够执行 printDescending 20 并让它输出 20、19、18...2、1、0。但我收到此错误:

>     • Non type-variable argument
>         in the constraint: Num ((a -> m a) -> a1 -> m1 a1)
>       (Use FlexibleContexts to permit this)
>     • When checking the inferred type
>         printDescending :: forall (m :: * -> *) a (m1 :: * -> *) a1.
>                            (Ord a1, Num ((a -> m a) -> a1 -> m1 a1), Num (m1 a1), Num a1,
>                             Monad m1, Monad m) =>
>                            a1 -> m1 a1 Failed, modules loaded: none.

我想这可能是我的语法问题。有人有一些见解吗?

您已经显示:

printDescending n = if n > 0
                    then printDescending n - 1 return n
                    else return n - 1

让我们先看看这是如何解析的:

printDescending n = if n > 0
                    then (printDescending n) - (1 return n)
                    else (return n) - 1

可能不是您想要的,是吧?请注意中缀运算符,例如 - 绑定不如函数应用程序紧密。另请参阅 'return' 并不特别 - 它也只是函数应用程序。最后,您实际上并没有包含对任何类型的 print 命令的调用,因此无论您使用哪种语言,我都不希望它起作用。

解决这些问题,让我们首先提供一个类型签名(有点像 C 中的函数原型):

printDescending :: Int -> IO ()

所以 printDecending 接受一个参数,一个 Int,并做一些 IO。 () 被称为 "unit" 并且对于了解 C 的程序员的第一课,您应该能够在心理上将其视为 void 并且没问题。

现在 body 怎么样?那么你的 if 陈述很好:

printDescending n =
    if n > 0
      then  ...
      else...

else 声明有点奇怪。即使修复了解析,你为什么要0-1?让我们只是 return 单位:

printDescending n =
    if n > 0
      then ...
      else return ()

现在对于 then 分支,您确实需要两件事。 1. 打印值 2. 对下一个最小值递归调用 printDecending。有一种符号,do,我们将使用它来对两个 IO 操作进行排序,但除此之外,这两个任务直接转换为命令:

printDescending n =
    if n > 0
      then do print n
              printDescending (n - 1)
      else return ()

现在让我们再做一步。由于这不是 C/Java/etc 并且是一种函数式语言,因此人们会期待一种声明式的风格。为此,让我们使用 guards 而不是 if 语句来声明 printDescending:

printDescending n
    | n > 0 = do print n
                 printDescending (n-1)
    | otherwise = return ()

备选方案

现在我将介绍一些替代方案,仅作为示例。

如果您不关心负值,那么我们可以在零处终止,例如:

printDescending 0 = return ()
printDescending n =
     do print n
        printDescending (n-1)

或者我们可以使用列表理解来构建值列表,然后对列表的每个元素执行 print。我们可以用 forM_ 来做到这一点(另见更惯用的 mapM_),它对列表的每个元素执行一个操作并丢弃结果,returning unit.

printDescending n = forM_ [n,n-1..1] print

最后,如果您想进行更快速的交流,请考虑加入 irc.freenode.net 上的#haskell 频道。那里有一个很棒的团队,我认识的许多 Haskell 程序员都是从那里开始的。

除了 Thomas 的详细回答外,我觉得以下是您想要的:

printDescending :: Integer -> IO ()
printDescending n = do
  print n
  if n > 0
     then printDescending (n - 1)
     else return ()

诚然,return () 一开始可能看起来很奇怪。

更实用的方法可能是这样的:

printDescending :: Integer -> IO ()
printDescending n = mapM_ print [n, (n-1) .. 0]

快速提示:我建议添加函数签名(如果您知道它是什么),因为它有助于指导编译器的错误消息。