我猜你得到的错误是试图简单地将foldr 切换到foldl':
myConcat xs ys = foldl' (:) ys xs
产生错误(使用我的 Hugs REPL):
ERROR - Type error in application
*** Expression : foldl' (:) xs ys
*** Term : (:)
*** Type : a -> [a] -> [a]
*** Does not match : [a] -> a -> [a]
注意最后两行(提供的类型和预期的类型)[a] 和 a 的位置是相反的。这意味着我们需要一个类似于(:) 的函数,但它的参数顺序相反。
Haskell 有一个函数可以为我们做这件事:flip 函数。基本上,flip 等价于
flip :: (a -> b -> c) -> (b -> a -> c)
flip f y x = f x y
也就是说,flip 将二进制函数作为参数,并返回另一个二进制函数,其参数与原始函数相反(“翻转”)。因此,虽然(:) 的类型为a -> [a] -> [a],但我们看到flip (:) 的类型为[a] -> a -> [a],使其成为foldl' 的参数的完美候选者。
使用flip,我们现在有这个代码:
myConcat xs ys = foldl' (flip (:)) ys xs
这是因为foldl' 的类型为(a -> b -> c) -> a -> [b] -> c
使用参数[1..5] 和[6..10] 运行它,我们得到[5,4,3,2,1,6,7,8,9,10] 的结果,这几乎是我们想要的。唯一的问题是第一个列表在结果中倒退了。添加对reverse 的简单调用为我们提供了myConcat 的最终定义:
myConcat xs ys = foldl' (flip (:)) ys (reverse xs)
查看这个过程会发现编写 Haskell 代码时经常出现的一件好事:当您遇到问题时,您可以一次解决一个(小)步骤。当您已经有一个工作实现并且您只是尝试编写另一个时,尤其如此。需要注意的一件大事是,如果您更改了实现的一部分(在本例中,将foldr 更改为foldl'),那么许多其他需要的更改就会从类型定义中消失。剩下的少数是更正问题,可以通过运行测试用例或通过查看所使用函数的确切性质来轻松找到。
PS:任何可以更新最后一行代码的 Haskell 人员,请随意这样做。虽然它并不可怕,但我觉得它不是很漂亮。不幸的是,我对 Haskell 还不是很了解。