我只是把我的脚趾浸入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!
我不仅仅是寻找答案,而是更多地解释它。
答案 0 :(得分:16)
我不仅仅是寻找答案,而是更多地解释它。
在=的左侧,您使用sum
作为应用于x
的函数。编译器不知道x
的类型,因此编译器使用类型变量a
代表“x
的类型”。此时,编译器也不知道函数sum
的结果类型,因此它选择另一个类型变量t
来表示结果类型。现在在左侧,编译器认为x
的类型为a -> t
(函数接受a
并返回t
)。
在=的右侧,您可以添加x
和sum
。在Haskell中可以添加各种数字,但只有在具有相同类型的情况下才能添加两个数字。因此,编译器假定sum
与x
具有相同的类型,即类型a
。
但是在Haskell中,标识符有一种类型 - 可能是whangdilly复杂类型,但仍然是一种类型。这包括sum
,其符号两侧的类型应该相同,因此编译器会尝试求解等式
a = a -> t
a
和t
没有解决此等式的值。根本无法完成。没有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]
中求和=(+)