博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
函数式点滴--高阶函数及抽象
阅读量:5894 次
发布时间:2019-06-19

本文共 3733 字,大约阅读时间需要 12 分钟。

介绍

高阶函数, 即higher-order function(HOF), 接受函数为参数或返回函数的函数

javascript中函数是一等公民(first-class),意味着它可以

  • 用变量命名
  • 提供给函数作为参数
  • 由函数作为结果返回 ..

其实我们平时已经不知不觉地在使用它了,比如map, reduce, filter...

这里, 就想聊一个例子...

一个求平方根的例子

小姐姐: 求平方根? Math.sqrt

我: 额,这里想说的是,不使用Math.sqrt
小姐姐: 你想说什么?
我: 小姐姐,我想...
小姐姐: 你个老男人,年级比我还大,还天天叫我小姐姐,不行,你得请我喝奶茶
我: 额,我们一起学完sqrt,再说吧...

牛顿迭代

先准备一点数学知识, 牛顿迭代法(别名如牛顿-拉夫逊), 通俗理解

假设f(x) = 0在x0附近有一个根,那么使用上面的迭代式,依次计算x1, x2, x3.... 最终将无线逼近方程的根(如果迭代是收敛的)

再通过一个动图感受一下

感兴趣的同学可以浏览

第一个sqrt

好了,我们现在不就是想求√x的值嘛, 即 y = √x

两边平方后,其实就是求方程y^2 - x = (y - √x)(y + √x) = 0的一个正根

令f(y) = y^2 - x 对y求导f'(y) =2y

应用牛顿迭代公式

好了,我们可以看下√2的计算过程,

猜测 平均值
1 2/1 = 2 (2 + 1) / 2 = 1.5
1.5 2 / 1.5 = 1.33333 (1.33333 + 1.5) / 2 = 1.4167
1.4167 2 / 1.4167 = 1.418 (1.4167 + 1.418) / 2 = 1.4142
1.4142 ... ...

写代码吧

function sqrt(x, guess = 1.0) {  // 如果guess足够好,则停止计算  if (goodEnough(guess, x)) {    return guess  } else {    return sqrt(x, improve(guess, x))  }}// 改进guess值function improve(guess, x) {  return average(guess, x / guess)}// 这里我们认为 |guess^2 - x| < 0.001 即guess已经足够好function goodEnough(guess, x) {  return abs(square(guess) - x) < 0.001}function average(num1, num2) {  return (num1 + num2) / 2}function square(num) {  return num * num}function abs(num) {  if (num >= 0) {    return num  } else {    return 0 - num  }}let r = sqrt(9)// 3.00009155413138console.log(r)复制代码

第二个sqrt

再来一点数学知识, 找出函数的不动点 数x称为f的不动点,如果x满足方程f(x) = x

思路: 某些函数,通过从某个初始猜测出发,反复第应用f, f(x), f(f(x)), f(f(x)) .... 直到值变化不大时,就可以找到它的一个不动点

这里fixedPoint函数接收函数为参数,所以它是一个高阶函数

const toleracne = 0.0001function abs(num) {  if (num >= 0) {    return num  } else {    return 0 - num  }}function closeEnough(v1, v2) {  return abs(v1 - v2) < toleracne}function tryGuess(f, guess) {  let next = f(guess)  if (closeEnough(guess, next)) {    return next  } else {    return tryGuess(f, next)  }}function fixedPoint(f, firstGuess = 1.0) {  return tryGuess(f, firstGuess)}// 我们来求余弦函数的不动点// r1为0.7390822985224023let r1 = fixedPoint(Math.cos)// 再来验证一下// 发现r2为0.7390870426953322let r2 = Math.cos(0.7390822985224023)复制代码

现在我们来将平方根的计算形式化为一个寻找不动点的计算过程

计算y = √x, 就是找到一个y使得y^2 = x, 其等价于 y = x / y, 所以我们只需寻找 y = x / y的不动点

遗憾的是,搜寻并不收敛。考虑初始猜测y1, 下一猜测y2 = x / y1, 而下一猜测y3 = x / y2 = y1。出现了往复震荡

采用"平均阻尼"的技术,帮助收敛,取y之后的下一猜测值为 (1/2)(y + x / y),即寻找 y = (1/2)(y + x / y)的不动点

function sqrt(x) {  return fixedPoint((y) => {    return average(y, x/y)  })}复制代码

而"平均阻尼"也是常用到的一个技术,所以我们也定义一下

它接收一个函数为参数,返回另一个函数

function averageDamp(f) {  return (x) => {    return average(x, f(x))  }}复制代码

利用averageDamp重做平方根

function sqrt(n) {  return fixedPoint(averageDamp((y) => {    return n / y  }))}复制代码

第三个sqrt

回到前面的牛顿迭代法。如果g(x)是可微的,那么g(x) = 0的一个解就是f(x)的一个不动点, 其中f(x) = x - g(x) / g'(x)

现在我们将牛顿法也定义为一个函数, 先定义下导数

const dx = 0.0001function deriv(g) {  return (x) => {    return (g(x + dx) - g(x)) / dx  }}复制代码

现在牛顿法就可以表述为一个求不动点的过程了

function newtonTransform(g) {  return (x) => {    return x - g(x) / deriv(g)(x)  }}function newtonMethod(g, guess = 1.0) {  return fixedPoint(newtonTransform(g), guess)}复制代码

于是,sqrt可以定义为

function sqrt(x) {  return newtonMethod((y) => {    return square(y) - x  })}复制代码

抽象

其实上面描述平方根,用了两种方式,一种方式是使用不动点搜寻,一种方式是使用牛顿法。而牛顿法本身也是一个不动点的计算过程。

所以我们将这也抽象成一个函数

function fixedPointOfTransform(g, transform, guess = 1.0) {  return fixedPoint(transform(g), guess)}复制代码

那么第二个sqrt可以更改为

function sqrt(x) {  return fixedPointOfTransform((y) => {    return x / y  }, averageDamp)}复制代码

那么第三个sqrt可以更改为

function sqrt(x) {  return fixedPointOfTransform((y) => {    return square(y) - x  }, newtonTransform)}复制代码

小结

我: 走吧,去买奶茶

小姐姐: 不去了,头疼...

今天通过一个例子,我们一起了解了下高阶函数,以及使用高阶函数进行抽象的过程

补充: 这个例子选自上世纪国外计算机学科某经典教材中...说得有点玄了

然后,数学部分可以淡化(知道结论不必去想推导过程),了解编程思想即可
例子还是给读者想象空间的, 比如上一小节名为"抽象", 但实际我们在提取"平均阻尼", "牛顿法", 其实已经在"抽象"了, 不是吗? 然后到底什么时候才要抽象,是不是要尽可能地抽象下去, 其实都值得思考。

转载地址:http://dpisx.baihongyu.com/

你可能感兴趣的文章
常见的海量数据处理方法
查看>>
Microsoft Windows 8.1 使用记录
查看>>
C语言博客作业03--函数
查看>>
web.xml 中CharacterEncodingFilter类的学习
查看>>
显示刚刚添加的最后一条数据,access,选择语句,select
查看>>
贪吃蛇逻辑代码
查看>>
实现c协程
查看>>
ASP.NET视频教程 手把手教你做企业论坛网站 视频教程
查看>>
[LeetCode] Meeting Rooms II
查看>>
从Swift学习iOS开发的路线指引
查看>>
3.1链表----链表(Linked List)入门
查看>>
[布局] bootstrap基本标签总结
查看>>
异步编程思想
查看>>
"数学口袋精灵"bug(团队)
查看>>
2017python第六天作业 面向对象 本节作业: 选课系统
查看>>
【找规律】Divide by Zero 2017 and Codeforces Round #399 (Div. 1 + Div. 2, combined) B. Code For 1...
查看>>
Scribes:小型文本编辑器,支持远程编辑
查看>>
为什么要使用 SPL中的 SplQueue实现队列
查看>>
文件的相关操作(创建、打开、写入、读出、重命名)
查看>>
品尝阿里云容器服务:用nginx镜像创建容器,体验基于域名的路由机制
查看>>