美文网首页
Haskell 03 高阶函数不仅仅是Lambda

Haskell 03 高阶函数不仅仅是Lambda

作者: 科学Jia | 来源:发表于2018-04-01 21:56 被阅读122次

--"为什么要努力"
--“因为我喜欢的东西很贵, 我想去的地方很远, 我爱的人超完美"

妹纸周玥翻唱的“下课等你”,感觉比周杰伦本尊唱的更好呢。

柯里函数

虽然我们前面看到的很多函数都是带了非常多的参数的,而本质上,这些函数执行的过程是这样的:它不会一次性取完所有参数,而是在每次调用时只取一个参数,并返回一个一元函数来取下一次参数。e.g.

ghci> max 4 5
5
ghci> (max 4) 5

上面这个简单的栗子中,max函数并不会直接输入2个参数,而是先取参数4,并返回一个一元函数,再取5这个参数,得到最后的值。

所以,依次类推,其实很多参数的函数都是柯里函数。

再举个栗子感受下:

multThree :: Int -> Int -> Int -> Int
multThree x y z = x * y * z

由上面说的柯里函数的本质,我们通过传入少数参数来调用这个函数,并因此创建新的函数multiTwo:

ghci>let multiTwo  =  multThree 9
ghci>multiTwo 3 4

首先给multThree只传入一个参数9,让它返回一个新的函数multiTwo,这个multiTwo需要传入2个参数。简单的说,我们只要以部分的参数来调用某个 函数,就可以得到一个部分应用函数,这个新的部分应用函数需要传入的参数,要和前面少传入的参数一致。

这里需要注意的是,我们如果不给这个部分应用函数复值新的名字,直接打印这个部分应用函数在终端,就出错。

ghci>multThree 9
这里会打印出一堆错误信息

这里一定要理解这个部分应用函数的原理,所以,我也不断粗体它。

有关map和filter的栗子

比较实用的函数有:map,zipWith, flip, filter。
这里举一个非常巧妙地体现这些函数的栗子,涉及到了map和filter,非常非常意思。

克拉兹序列(Collatz sequence)

该序列产生的定义如下:

  • 从任何自然数开始;
  • 如果是1,停止;
  • 如果是偶数,将它除以2;
  • 如果是奇数,将它乘以3然后加1;
  • 所得结果,然后重复上面算法;

从上面描述,我们最后会得到一个以1结尾的序列(列表),现在我们需要求解一个问题:分别以1和100之间的所有数做起始数,有多少个克拉兹序列的长度大于15?

第一步我们要写一个产生序列的函数:

chain :: Integer -> [Integer]
chain 1 = [1] 
chain n
         | even n = n : chain (n 'div' 2)
         | ood n = n: chain (n*3 + 1)

这是一个标准的递归函数,这个函数会止步于1。

输入一个起始值10,来测试下该序列函数:

ghci> chain 10
[10, 5, 16, 8, 4, 2, 1]

接下来,计算有多少个长度大于15的序列?

numLongChains :: Int
numLongChains = length (filter isLong  (map chain [1..100]))
     where isLong xs = length xs > 15

这里需要解释下,filter 函数取一个谓词(也就是一个条件)和一个列表,返回该列表中符合该条件的元素组成的列表。map函数是取一个函数和列表作为参数,它会将这个函数应用到该列表中的每个元素,并产生一个新的列表。

所以,上面的函数numLongChains中,map把[1..100]的序列应用到了chain这个函数中,并返回一个生成了克拉兹序列的列表,这个含有克拉兹序列的列表又作为filter函数中的列表参数,isLong函数的实现在where语句中,它定义了一个谓词(也就是一个条件)length xs > 15, 也就是这个列表中要过滤出长度大于15的克拉兹序列,并返回这个满足条件的新的列表,最后这个新列表由length函数完成计算。

lambda

lambda函数就是一次性匿名的函数
有时候,我们需要传给高阶函数一些特定功能的函数,就跟上面这个栗子一样,isLong就是我们需要的特殊功能的函数,这样我们就会用到lambda。
要使用lambda,就写一个*(因为这样看起来它比较像希腊字母lambda),后面跟参数,多个参数之间用空格分隔开,-> 后面是函数体*。通常我们习惯将整个lambda函数用括号括起来。

所以上面numLongChains用lambda实现是这样的:

numLongChains :: Int
numLongChains = length (filter (\xs -> length xs >15) (map chain [1..100]))

这样不用额外再去定义一个函数名和实现。

以上。

贴出周玥小妹妹的照片:


Haskell 03 高阶函数不仅仅是Lambda

相关文章

网友评论

      本文标题:Haskell 03 高阶函数不仅仅是Lambda

      本文链接:https://www.haomeiwen.com/subject/bovacftx.html