Haskell递减函数

时间:2016-09-05 04:33:48

标签: haskell recursion type-inference

我正在努力学习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.

我认为这可能是我的语法。任何人都有一些见解?

2 个答案:

答案 0 :(得分:8)

您已展示:

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

可能不是你想要的,是吧?请注意,中缀运算符(如-)的绑定程度低于函数应用程序。另请参阅'返回'并不特别 - 它也只是功能应用。最后,您实际上并没有对任何类型的print命令进行调用,因此无论您使用何种语言,我都不会期望它能够正常运行。

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

printDescending :: Int -> IO ()

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

现在身体怎么样?那么你的if声明很好:

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

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

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 /等,并且是一种功能语言,人们会期望一种声明式的风格。为此,让我们使用警卫而不是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_),它会对列表的每个元素执行操作并抛弃结果,返回单位。

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

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

答案 1 :(得分:3)

除了托马斯的详细答案之外,我还觉得以下是你的目标:

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]

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