前几天同事发了这么一段代码:

(fn =>
(f => f(f))(f => fn(n => f(f)(n))))(g => n =>
[1, 2].indexOf(n) > -1 ? 1 : g(n - 1) + g(n - 2)
)(10);

你看这段代码时,一定是这样的心情:


好端端的 斐波那契 是怎么变成这样的,因吹斯听,我们来回放一下。

从正常的写法开始:

const fib = n =>
[1, 2].indexOf(n) >= 0
? 1
: fib(n - 1) + fib(n - 2);

为了让上面看起来不像递归,改写一下,把递归调用改成调用参数 g

const wrappedFib = g => n =>
[1, 2].indexOf(n) >= 0
? 1
: g(n - 1) + g(n - 2);

不管 g 传什么,例如就传 null,1,2 两项都可以计算了,因为压根和 g 无关。

wrappedFib(null)(1);
wrappedFib(null)(2);

如果要计算第 3 项,那么 g 可以是 wrappedFib(null)

let g = wrappedFib(null);
wrappedFib(g)(3);

同理,第 4 项:

let g = wrappedFib(wrappedFib(null));
wrappedFib(g)(4);

第 5 项......第 N 项我就不列了。


看起来需要构造一个 g,它由无限层 wrappedFib 组成。

递归的思想:

const g = n => wrappedFib(g)(n);

运行一下试试吧:

const wrappedFib = g => n =>
[1, 2].indexOf(n) >= 0
? 1
: g(n - 1) + g(n - 2);
const g = n => wrappedFib(g)(n);
console.log(wrappedFib(g)(10));

g 本身是由无限层 wrappedFib 组成的,所以 wrappedFib(g)g 是等价的。

因此也可以直接调 console.log(g(10))


const g = n => wrappedFib(g)(n);

又看到了明显的递归对不对,试着把它藏起来。思想跟刚开始一样,通过参数传进来。

这段要花点时间理解。

const g = (f => n =>
wrappedFib(f(f))(n))(f => n =>
wrappedFib(f(f))(n)
);

函数本身和函数传参一样,换个写法:

const g = (f => f(f))(f => n =>
wrappedFib(f(f))(n)
);

能到这里,我们和最终的代码已经很接近了。

g 中的 wrappedFib 去掉,通过参数 fn 传进来。

const gWaitForWrappedFib = fn =>
(f => f(f))(f => n => fn(f(f))(n));
const g = gWaitForWrappedFib(wrappedFib);

好了,去掉常量的定义,全部连起来吧:

(fn =>
(f => f(f))(f => fn(n => f(f)(n))))(g => n =>
[1, 2].indexOf(n) > -1 ? 1 : g(n - 1) + g(n - 2)
)(10);

拆解这段代码挺烧脑,膜拜一下代码的作者。


更新

读《The Little Schemer》时发现:这段代码和 Y-Combinator 有关。Mark,以后再写个续。

How to Make Fibonacci Confusing的更多相关文章

  1. 算法与数据结构(九) 查找表的顺序查找、折半查找、插值查找以及Fibonacci查找

    今天这篇博客就聊聊几种常见的查找算法,当然本篇博客只是涉及了部分查找算法,接下来的几篇博客中都将会介绍关于查找的相关内容.本篇博客主要介绍查找表的顺序查找.折半查找.插值查找以及Fibonacci查找 ...

  2. #26 fibonacci seqs

    Difficulty: Easy Topic: Fibonacci seqs Write a function which returns the first X fibonacci numbers. ...

  3. 关于java的递归写法,经典的Fibonacci数的问题

    经典的Fibonacci数的问题 主要想展示一下迭代与递归,以及尾递归的三种写法,以及他们各自的时间性能. public class Fibonacci { /*迭代*/ public static ...

  4. 斐波拉契数列(Fibonacci) 的python实现方式

    第一种:利用for循环 利用for循环时,不涉及到函数,但是这种方法对我种小小白来说比较好理解,一涉及到函数就比较抽象了... >>> fibs = [0,1] >>&g ...

  5. fibonacci数列(五种)

    自己没动脑子,大部分内容转自:http://www.jb51.net/article/37286.htm 斐波拉契数列,看起来好像谁都会写,不过它写的方式却有好多种,不管用不用的上,先留下来再说. 1 ...

  6. POJ3070 Fibonacci[矩阵乘法]

    Fibonacci Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 13677   Accepted: 9697 Descri ...

  7. Fibonacci 数列算法分析

    /************************************************* * Fibonacci 数列算法分析 ****************************** ...

  8. 算法系列:Fibonacci

    Copyright © 1900-2016, NORYES, All Rights Reserved. http://www.cnblogs.com/noryes/ 欢迎转载,请保留此版权声明. -- ...

  9. UVa #11582 Colossal Fibonacci Numbers!

    巨大的斐波那契数 The i'th Fibonacci number f (i) is recursively defined in the following way: f (0) = 0 and  ...

随机推荐

  1. Windows Server 2016-DHCP服务器审核日志大小调整

    DHCP Server服务在%windir%\System32\DHCP或"%SystemRoot%\System32\DHCP"文件夹下存放了一个审核日志.审核日志文件名称是基于 ...

  2. LOJ #6050. 「雅礼集训 2017 Day11」TRI

    完全不会的数学神题,正解留着以后填坑 将一个口胡的部分分做法,我们考虑计算格点多边形(包括三角形)面积的皮克公式: \[S=a+\frac{1}{2}b-1\text({a为图形内部节点个数,b为边界 ...

  3. 探索JS引擎工作原理

    JavaScript 从定义到执行,JS引擎在实现层做了很多初始化工作,因此在学习 JS 引擎工作机制之前,我们需要引入几个相关的概念:执行环境栈.全局对象.执行环境.变量对象.活动对象.作用域和作用 ...

  4. git命令设置

    初始化为git项目: cd .../project  //切换到项目目录下 git init  //把project初始化为git项目 git remote add origin https://gi ...

  5. AJAX快速上手和基本核心

    一.快速上手AJAX 使用ajax的过程可以类比平常我们访问网页过程 1.创建一个XMLHttpRequest类型的对象------相当于打开了浏览器 var xhr = new XMLHttpReq ...

  6. mongo connections url string 的问题

    摘要 driver 连接Mongo DB的url其实很简单,就是几个变量拼接成一个url,和关系型数据库没什么不同.但是因为mongo有单个instance和replicaSet不同的部署策略,还有m ...

  7. redis 初识

    架构 sharding redis 集群是主从式架构,数据分片是根据hash slot(哈希槽来分布) 总共有16384个哈希槽,所以理论上来说,集群的最大节点(master) 数量是16384个.一 ...

  8. Spring之旅第四篇-注解配置详解

    一.引言 最近因为找工作,导致很长时间没有更新,找工作的时候你会明白浪费的时间后面都是要还的,现在的每一点努力,将来也会给你回报的,但行好事,莫问前程!努力总不会有错的. 上一篇Spring的配置博客 ...

  9. AutoStartUtil【打开自启动设置界面】

    参考资料 Android6.0 打开自启动管理页面(华为.小米) Android打开自启动设置页面 Android 机型设置自启动的跳转界面 代码 注意:需要搭配<RomUtil[Android ...

  10. ConfirmCancelBottomSheetDialog【确认取消底部对话框】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 继承BottomSheetDialog,实现简单的确认取消对话框样式. 效果图 代码分析 ConfirmCancelBottomSh ...