文件夹有4个参数?

时间:2019-04-14 04:56:04

标签: haskell

我正在努力理解为什么从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是一个列表元素,而不是一个函数...

2 个答案:

答案 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