学习Haskell映射,折叠,循环和递归

时间:2010-06-13 21:31:13

标签: haskell recursion types functional-programming

我只是把我的脚趾浸入Haskell的世界,作为我的编程启蒙之旅的一部分(从程序到OOP再到并发到现在的功能)。

我一直在尝试在线Haskell Evaluator

但是我现在遇到了一个问题:

创建一个简单的函数,它给出一组数字的总和。

在程序语言中,这对我来说很容易(使用递归)(c#):

private int sum(ArrayList x, int i)
{
  if (!(x.Count < i + 1)) {
        int t = 0;

        t = x.Item(i);
        t = sum(x, i + 1) + t;
        return t;
    }
}

一切都很好,但是我在Haskell的失败尝试是这样的:

let sum x = x+sum  in map sum [1..10]

这导致以下错误(来自上述网站):

Occurs check: cannot construct the infinite type: a = a -> t

请记住,我过去30分钟只使用过Haskell!

我不仅仅是寻找答案,而是更多地解释它。

4 个答案:

答案 0 :(得分:16)

  

我不仅仅是寻找答案,而是更多地解释它。

在=的左侧,您使用sum作为应用于x的函数。编译器不知道x的类型,因此编译器使用类型变量a代表“x的类型”。此时,编译器也不知道函数sum的结果类型,因此它选择另一个类型变量t来表示结果类型。现在在左侧,编译器认为x的类型为a -> t(函数接受a并返回t)。

在=的右侧,您可以添加xsum。在Haskell中可以添加各种数字,但只有在具有相同类型的情况下才能添加两个数字。因此,编译器假定sumx具有相同的类型,即类型a

但是在Haskell中,标识符有一种类型 - 可能是whangdilly复杂类型,但仍然是一种类型。这包括sum,其符号两侧的类型应该相同,因此编译器会尝试求解等式

a = a -> t

at没有解决此等式的值。根本无法完成。没有a使得a等于将自己接受为参数的函数。因此出现了错误消息

cannot construct the infinite type: a = a -> t

即使有了所有的解释,它也不是一个很好的错误信息,是吗?

欢迎来到Haskell: - )


P.S。你可能喜欢尝试“Helium,用于学习Haskell”,它为初学者提供了更好的错误信息。

答案 1 :(得分:12)

'sum'获取值列表并将其减少为单个值。您可以将其写为显式循环(请记住,Haskell没有循环关键字,但使用递归)。请注意,根据列表的形状,定义有两个部分:

mysum []     = 0
mysum (x:xs) = x + mysum xs

或者更有效率,采用tail-recursive风格:

mysum xs = go 0 xs
   where
      go n []     = n
      go n (x:xs) = go (n+x) xs

然而,Haskell有一个丰富的控制结构库,可以在惰性列表上运行。在这种情况下,列表的减少可以使用reduce函数完成:折叠。

所以mysum可以写成:

mysum xs  = foldr (+) 0 xs

例如:

Prelude> foldr (+) 0 [1..10]
55

你的错误是使用地图,它一次转换一个列表,一个元素,而不是 fold

我建议你先介绍一下Haskell,也许是“Programming in Haskell”,以了解函数式编程的核心概念。其他好的介绍材料被描述为in this question.

答案 2 :(得分:1)

你需要阅读一本好的教程,有很多大的误解。

首先,我要假设你的意思是列表,而不是数组。数组存在于Haskell中,但它们不是你在初学者级别遇到的。 (更不用说你使用的是[1..10],这是一个数字1到10的列表。)

你想要的功能实际上是内置的,并且称为sum,所以我们必须调用别的东西,new_sum:

new_sum [] = 0
new_sum (h:t) = h + (sum t)

答案 3 :(得分:0)

让我们看看第一部分:

let sum x = x+sum 

在这种情况下,总和的类型是什么?它需要一个数字并返回一个函数,该函数接受一个数字,如果你已经写了它,则返回一个带有数字等的函数 设sum x = + x 你会有一个带数字的函数并返回函数+ x。 和 让sum = +  将返回一个带有两个整数并添加它们的函数。

现在让我们来看看第二部分。  在地图总和[1..10]  map采用一个参数的函数并将其应用于列表的每个元素。没有空间在那里楔入累加器,所以让我们看看其他列表函数,特别是foldl,foldr。这两个参数都是一个列表和一个起始值的两个参数的函数。 foldl和foldr之间的区别在于它们开始的一面。 l左边是1 + 2 + 3等,右边是10 + 9 + 8等。

在foldl sum 0 [1..10]

中求和=(+)