您没有分享究竟是什么不工作 - Haskell(或者更确切地说是 GHC)的编译时错误有时看起来有点难以理解,但如果您知道如何解释它们,它们总是充满了信息。这里的错误是:
<interactive>:4:30: error:
* Couldn't match expected type `[Int]' with actual type `Int'
* In the first argument of `(++)', namely `x'
In the expression: x ++ fixpointL f (f x)
In the expression: [x ++ fixpointL f (f x)]
<interactive>:4:30: error:
* Couldn't match expected type `Int' with actual type `[Int]'
* In the expression: x ++ fixpointL f (f x)
In the expression: [x ++ fixpointL f (f x)]
In an equation for `fixpointL':
fixpointL f x
| x == (f x) = [x]
| otherwise = [x ++ fixpointL f (f x)]
如您所见,类型不匹配有两个不同的错误 - 但它们实际上密切相关。两者都非常清楚到底出了什么问题。
在第一个中,它抱怨x ++ fixpointL f (f x) 中的x 应该是[Int](Ints 的列表),但实际上是Int。这是有道理的——x 是函数的第二个参数,类型签名声明它是Int。但是您正在尝试对其应用 (++) 运算符,并且此运算符的类型为:
Prelude> :t (++)
(++) :: [a] -> [a] -> [a]
- 也就是说,它接受两个列表(可以包含任何元素),并返回另一个相同类型的列表。
Int 肯定不是一个列表,所以难怪 GHC 无法理解这一点。
至于第二个错误 - 它告诉您表达式 x ++ fixpointL f (f x) 应该是 Int 但实际上是一个列表 ([Int])。后者是因为它本质上是在假装您将修复第一个错误并将x 变成它所期望的[Int] - 那么表达式x ++ fixpointL f (f x) 确实是Ints 的列表(即@ 987654339@)。但是您已将其括在方括号中:
[x ++ fixpointL f (f x)]
这意味着一个仅包含一个元素的列表,该元素为x ++ fixpointL f (f x),正如我刚刚解释的那样,目前假定其类型为[Int]。但是由于这是您的函数的返回值,并且您已将返回类型声明为[Int],这意味着[...] 的内容必须是零个或多个@ 类型的值(在本例中为一个) 987654345@。这就解释了另一种类型的不匹配。
我们如何修复它们?似乎您想要获取 fixpointL f (f x) 的输出 - 我们假设它是 Ints 的列表(您的类型签名说是,所以这是 GHC 必须假设的),并附加开始值,x,放在它的前面。正确的写法,假设你想使用(++)操作符,是这样的:
[x] ++ fixPointL f (f x)
这将获取仅包含x 的单例列表和递归调用的结果,并将它们放在一个列表中。这会按预期进行类型检查、编译和工作:
Prelude> fixpointL (^2) 1
[1]
Prelude> fixpointL (*2) 1
[1,2,4,8,16,32,64,128,256,512,...]
(显然,在第二个示例中,我尽可能快地杀死了它,在我可以之前的实际输出要长得多,但你明白了。)
您可以改进函数的最后一种方法是使用“cons”运算符(:),其目的是在列表的开头添加一个元素,而无需先构造一个单例列表。由于 Haskell 的列表是简单的链表,其中每个元素都包含指向下一个元素的指针,因此这始终是一种有效的操作(与将项目附加到末尾相反,这需要与列表长度成正比的时间)。虽然这里的性能差异很小,但它仍然被认为更惯用。所以这是工作的惯用版本:
fixpointL :: (Int -> Int) -> Int -> [Int]
fixpointL f x | x == (f x) = [x]
| otherwise = x : fixpointL f (f x)