Function Haskell:带函数参数的箭头优先级
我是一个相对有经验的Haskell程序员,有几个小时的经验,所以答案可能是显而易见的 在观看了Haskell的一段经历后,当Simon解释appendFunction Haskell:带函数参数的箭头优先级,function,haskell,recursion,arguments,operator-precedence,Function,Haskell,Recursion,Arguments,Operator Precedence,我是一个相对有经验的Haskell程序员,有几个小时的经验,所以答案可能是显而易见的 在观看了Haskell的一段经历后,当Simon解释append(++)函数如何使用其参数时,我迷路了 所以 首先,他说,(++)::[a]->[a]->[a]可以理解为一个函数,它获取两个列表作为参数,并在最后一个箭头后返回一个列表)。但是,他补充说,实际上,类似的情况会发生:(++):[a]->([a]->[a]),函数只接受一个参数并返回一个函数 我不确定返回的函数闭包如何获得第一个列表,因为它也需要一个
(++)
函数如何使用其参数时,我迷路了
所以
首先,他说,(++)::[a]->[a]->[a]
可以理解为一个函数,它获取两个列表作为参数,并在最后一个箭头后返回一个列表)。但是,他补充说,实际上,类似的情况会发生:(++):[a]->([a]->[a])
,函数只接受一个参数并返回一个函数
我不确定返回的函数闭包如何获得第一个列表,因为它也需要一个参数
在演示的下一张幻灯片上,我们有以下实现:
(++) :: [a] -> [a] -> [a]
[] ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)
如果我认为(++)接收两个参数并返回一个列表,那么这段代码以及递归就足够清楚了
如果我们认为<代码>(++)<代码>只接收一个参数并返回一个列表,那么<代码> ys>代码>来自哪里?返回的函数在哪里?
您对如何工作感到困惑。 考虑(++)
的以下函数定义
接受两个参数,生成一个列表:
(++) :: [a] -> [a] -> [a]
[] ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)
(++) :: [a] -> ([a] -> [a])
(++) [] = id
(++) (x:xs) = (x :) . (xs ++)
接受一个参数,生成一个函数接受一个列表并生成一个列表:
(++) :: [a] -> [a] -> [a]
[] ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)
(++) :: [a] -> ([a] -> [a])
(++) [] = id
(++) (x:xs) = (x :) . (xs ++)
如果仔细观察,这些函数将始终生成相同的输出。通过删除第二个参数,我们将返回类型从[a]
更改为[a]->[a]
- 如果我们向
提供两个参数,则得到类型为(++)
[a]
- 如果我们只提供一个参数,则得到类型为
[a]->[a]
这就是所谓的函数咖喱。我们不需要为具有多个参数的函数提供所有参数。如果我们提供的参数少于参数总数,那么我们得到的不是一个“具体”的结果(
[a]
),而是一个可以接受其余参数的函数([a]->[a]
)。理解这一点的诀窍是所有haskell函数最多只接受一个参数,只是类型签名和语法糖中的隐式括号使它看起来好像有更多的参数。以++
为例,以下转换都是等效的
xs ++ ys = ...
(++) xs ys = ...
(++) xs = \ys -> ...
(++) = \xs -> (\ys -> ...)
(++) = \xs ys -> ...
另一个快速示例:
doubleList :: [Int] -> [Int]
doubleList = map (*2)
这里我们有一个单参数的函数doubleList
,没有任何显式参数。这就相当于写作
doubleList x = map (*2) x
或以下任何一项
doubleList = \x -> map (*2) x
doubleList = \x -> map (\y -> y * 2) x
doubleList x = map (\y -> y * 2) x
doubleList = map (\y -> y * 2)
双重列表的第一个定义是用通常称为无点表示法编写的,之所以称之为无点表示法,是因为在支持它的数学理论中,参数被称为“点”,因此无点表示法是“无参数”
一个更复杂的例子:
func = \x y z -> x * y + z
func = \x -> \y z -> x * y + z
func x = \y z -> x * y + z
func x = \y -> \z -> x * y + z
func x y = \z -> x * y + z
func x y z = x * y + z
现在,如果我们想完全删除对参数的所有引用,我们可以使用执行函数组合的
运算符:
func x y z = (+) (x * y) z -- Make the + prefix
func x y = (+) (x * y) -- Now z becomes implicit
func x y = (+) ((*) x y) -- Make the * prefix
func x y = ((+) . ((*) x)) y -- Rewrite using composition
func x = (+) . ((*) x) -- Now y becomes implicit
func x = (.) (+) ((*) x) -- Make the . prefix
func x = ((.) (+)) ((*) x) -- Make implicit parens explicit
func x = (((.) (+)) . (*)) x -- Rewrite using composition
func = ((.) (+)) . (*) -- Now x becomes implicit
func = (.) ((.) (+)) (*) -- Make the . prefix
因此,正如您所看到的,有许多不同的方法可以使用不同数量的显式“参数”来编写特定函数,其中一些非常可读(即
func x y z=x*y+z
),而另一些方法只是一堆毫无意义的符号(即func=()(+)(*)也许这会有所帮助。首先,让我们不用运算符符号来编写它,这可能会让人困惑
append :: [a] -> [a] -> [a]
append [] ys = ys
append (x:xs) ys = x : append xs ys
我们可以一次应用一个参数:
appendEmpty :: [a] -> [a]
appendEmpty = append []
我们也可以写出来
appendEmpty ys = ys
appendOne ys = 1 : append [] ys
从第一个方程式
如果我们应用非空的第一个参数:
-- Since 1 is an Int, the type gets specialized.
appendOne :: [Int] -> [Int]
appendOne = append (1:[])
我们本可以写下同样的内容
appendEmpty ys = ys
appendOne ys = 1 : append [] ys
从第二个等式开始。查找currying:在大多数函数式编程语言中,不存在函数包含多个参数的情况。“二进制函数”是一个从技术上讲不正确的术语,用于表示接受一个参数(第一个)并返回一个函数的函数。此返回函数接受一个参数(“第二个”参数),最后返回结果。例如,(++)[1,2]
是一个可以应用于[4,5]
以生成[1,2,4,5]
的函数。好吧,我可以认为函数是lambda函数的别名。未传递参数,但按以下顺序填充/替换:add=\x y->x+y
,add3 4
(\x y->x+y)3 4
,但我了解到无点样式是最受欢迎的样式。有没有办法用高阶函数编写最后一行的func=
行?@D.Naesuko无点样式只有在看起来更清晰时才是首选。这只是删除一个函数参数时的情况,很少是删除多个函数参数时的情况。