我对Haskell和FP很新.我已经阅读了许多描述currying的文章,但我还没有找到它如何实际工作的解释.
这里是一个功能:(+) :: a -> (a -> a)
如果我这样做(+) 4 7
,该函数将4
返回一个函数,7
并返回11
.但是会发生什么4
?第一个功能是做什么用的4
?怎么(a -> a)
办7
?
当我考虑更复杂的功能时,事情变得更加混乱:
max' :: Int -> (Int -> Int)
max' m n | m > n = m
| otherwise = n
Run Code Online (Sandbox Code Playgroud)
什么(Int -> Int)
比较它的参数?它只需要一个参数,但它需要两个参数m > n
.
alt*_*ive 12
这有帮助吗?
max' = \m -> \n -> if (m > n)
then m
else n
Run Code Online (Sandbox Code Playgroud)
写成lambdas.max'是一个lambda的值,它本身返回一个给定m的lambda,返回值.
因此max'4是
max' 4 = \n -> if (4 > n)
then 4
else n
Run Code Online (Sandbox Code Playgroud)
Mir*_*lov 12
Haskell作为一种函数式语言,支持高阶函数(HOF).在数学中,HOF被称为函数,但是你不需要任何数学来理解它们.在通常的命令式编程中,如在Java中,函数可以接受值,如整数和字符串,对它们执行某些操作,并返回其他类型的值.
但是,如果函数本身与值没有区别,你可以接受一个函数作为参数或从另一个函数返回它?f a b c = a + b - c
是一个无聊的功能,总结a
和b
再substracts c
.但功能可能会更有趣,如果我们能概括它,如果我们会想有时总结a
和b
,但有时乘?或者除以c
而不是减去?
请记住,(+)
只是2个数字的函数返回一个数字,没有什么特别之处,所以返回数字的2个数字的任何函数都可以代替它.写作g a b c = a * b - c
,h a b c = a + b / c
等等只是不削减对我们来说,我们需要一个通用的解决方案,我们的程序员毕竟!这是如何在Haskell中完成的:
let f g h a b c = a `g` b `h` c in f (*) (/) 2 3 4 -- returns 1.5
Run Code Online (Sandbox Code Playgroud)
你也可以返回功能.下面我们创建一个接受函数和参数的函数,并返回另一个函数,该函数接受一个参数并返回一个结果.
let g f n = (\m -> m `f` n); f = g (+) 2 in f 10 -- returns 12
Run Code Online (Sandbox Code Playgroud)
甲(\m -> m `f` n)
构建体是匿名函数 1个论点m
适用f
于m
和n
.基本上,当我们调用时,g (+) 2
我们创建一个参数的函数,它只是为它收到的任何东西加2.所以let f = g (+) 2 in f 10
等于12,let f = g (*) 5 in f 5
等于25.
Currying是一种将多个参数的函数转换为1参数的函数的技术,该函数返回1参数的函数,该函数返回1参数的函数...直到它返回一个值.它比听起来容易,例如我们有2个参数的函数,比如(+)
.
现在假设你只能给它一个参数,它会返回一个函数吗?您可以稍后使用此函数将此第一个参数(现在包含在此新函数中)添加到其他内容中.例如:
f n = (\m -> n - m)
g = f 10
g 8 -- would return 2
g 4 -- would return 6
Run Code Online (Sandbox Code Playgroud)
猜猜看,Haskell默认会调整所有函数.从技术上讲,Haskell中没有多个参数的函数,只有一个参数的函数,其中一些可能返回一个参数的新函数.
从类型来看很明显.:t (++)
在解释器中写入,其中(++)
一个函数将两个字符串连接在一起,它将返回(++) :: [a] -> [a] -> [a]
.类型不是[a],[a] -> [a]
,但是[a] -> [a] -> [a]
,意味着(++)
接受一个列表并返回类型的函数[a] -> [a]
.这个新函数可以接受另一个列表,它最终将返回一个新的类型列表[a]
.
这就是为什么Haskell中的函数应用程序语法没有括号和逗号,将Haskell f a b c
与Python或Java 进行比较的原因f(a, b, c)
.这不是一些奇怪的美学决定,在Haskell函数应用程序中从左到右,f a b c
实际上(((f a) b) c)
,这是完全有道理的,一旦你知道f
默认情况下是curry.
但是,在类型中,关联是从右到左,所以[a] -> [a] -> [a]
相当于[a] -> ([a] -> [a])
.它们在Haskell中是相同的,Haskell对它们完全相同.这是有道理的,因为当你只应用一个参数时,你会得到一个类型的函数[a] -> [a]
.
另一方面,检查类型map
:(a -> b) -> [a] -> [b]
,它接收一个函数作为它的第一个参数,这就是它有括号的原因.
要真正理解currying的概念,请尝试在解释器中查找以下表达式的类型:
(+)
(+) 2
(+) 2 3
map
map (\x -> head x)
map (\x -> head x) ["conscience", "do", "cost"]
map head
map head ["conscience", "do", "cost"]
Run Code Online (Sandbox Code Playgroud)
既然你已经理解了HOF和currying,那么Haskell会为你提供一些语法来缩短代码.当你用一个或多个参数调用一个函数来获取仍然接受参数的函数时,它被称为部分应用程序.
您已经明白,不是创建匿名函数,而是可以部分应用函数,因此(\x -> replicate 3 x)
您可以只编写而不是编写(replicate 3)
.但是如果你想要一个除法(/)
运算符而不是replicate
?对于中缀函数,Haskell允许您使用任一参数部分应用它.
这就是所谓的部分:(2/)
等同于(\x -> 2 / x)
和(/2)
等价于(\x -> x / 2)
.使用反引号,您可以获取任何二元函数的一部分:(2`elem`)
相当于(\xs -> 2 `elem` xs)
.
但请记住,默认情况下,任何函数都在Haskell中进行调整,因此总是接受一个参数,因此部分实际上可以与任何函数一起使用:让(+^)
一些奇怪的函数对4个参数求和,然后let (+^) a b c d = a + b + c in (2+^) 3 4 5
返回14.
编写简洁灵活代码的其他方便工具是组合和应用程序操作员.组合运算符(.)
链一起运行.应用程序操作员($)
只将左侧的函数应用于右侧的参数,因此f $ x
等效于f x
.但是($)
所有运算符的优先级最低,因此我们可以使用它来除去括号:f (g x y)
相当于f $ g x y
.
当我们需要将多个函数应用于同一个参数时,它也很有用:map ($2) [(2+), (10-), (20/)]
会产生[4,8,10]
.(f . g . h) (x + y + z)
,f (g (h (x + y + z)))
,f $ g $ h $ x + y + z
和f . g . h $ x + y + z
相当,但(.)
并($)
是不同的东西,所以读的Haskell:之间的差异.(点)和$(美元符号)和来自Learn You a Haskell的部分来理解差异.
归档时间: |
|
查看次数: |
5463 次 |
最近记录: |