我正在努力理解为什么从haskell.org exercise page类型检查中获取此代码(并用作列表反转功能)的原因:
myReverse :: [a] -> [a]
myReverse xs = foldr (\x fId empty -> fId (x : empty)) id xs []
我的第一点困惑是,foldr接受3个参数,而不是4个:
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
所以我猜myReverse
等同于:
myReverse xs = foldr ((\x fId empty -> fId (x : empty)) id) xs []
但是这也不应该起作用,因为在lambda中,x
是一个列表元素,而不是一个函数...
答案 0 :(得分:4)
这样想。每个函数只接受一个参数。它可能返回另一个函数(接受一个参数)。看起来像多参数调用的东西
f a b c
实际上被解析为
((f a) b) c
,即一串单参数函数应用程序。函数类型
f :: a -> b -> c -> d
可以分解为
f :: a -> (b -> (c -> d))
即返回函数的函数返回函数的函数。我们通常将其视为三个参数的函数。但是可以接受三个以上吗?是的,如果d
恰好是另一种函数类型。
这就是您的fold
示例所发生的。您作为foldr
的第一个参数传递的函数接受三个参数,这与接受两个参数并返回另一个函数完全相同。现在foldr
的(简化)类型是
(a -> b -> b) -> b -> [a] -> b
但是,如果您查看它的第一个参数,就会发现它是三个参数的函数。正如我们所看到的,它与一个函数完全相同,该函数接受两个参数并返回一个函数。因此b
恰好是函数类型。由于b
也是foldr
应用于三个参数时的返回结果
foldr (\x fId empty -> fId (x : empty)) id
它是一个函数,现在可以应用于另一个参数
(foldr (\x fId empty -> fId (x : empty)) id xs) []
我让您弄清楚b
到底是什么。
答案 1 :(得分:2)
首先,变量的命名是残酷的。我总是将r
用作文件夹的reducer函数的第二个参数,作为“递归结果”的助记符。 “ empty
”的含义太重了;最好使用一些中性的名称,以便在没有任何先入为主的概念的情况下更容易了解它的含义:
myReverse :: [a] -> [a]
myReverse xs = foldr (\x r n -> r (x : n)) id xs []
凭借文件夹的定义,
foldr f z (x:xs) === f x (foldr f z xs)
即
myReverse [a,b,c,...,z]
= foldr (\x r n -> r (x : n)) id [a,b,c,...,z] []
= (\x r n -> r (x : n)) a (foldr (\x r n -> r (x : n)) id [b,c,...,z]) []
= (\x r n -> r (x : n))
a
(foldr (\x r n -> r (x : n)) id [b,c,...,z])
[]
= let { x = a
; r = foldr (\x r n -> r (x : n)) id [b,c,...,z]
; n = []
}
in r (x : n)
= foldr (\x r n -> r (x : n)) id [b,c,...,z] (a : [])
= foldr (\x r n -> r (x : n)) id [b,c,...,z] [a]
= ....
= foldr (\x r n -> r (x : n)) id [c,...,z] (b : [a])
= foldr (\x r n -> r (x : n)) id [c,...,z] [b,a]
= ....
= foldr (\x r n -> r (x : n)) id [] [z,...,c,b,a]
= id [z,...,c,b,a]
我希望这个插图可以使事情变得更清楚。 reducer函数需要额外的参数,而foldr
会将其转化为行动...导致等价于
= foldl (\n x -> (x : n)) [] [a,b,c,...,z]
事实证明,myReverse
实现是使用等价
foldl (flip f) n xs === foldr (\x r -> r . f x) id xs n