前两天试刷CodeWars时,有道题一直没太明白思路,大概是一直按照传统函数式逻辑流程来思考所以没有了解这道题的立意。

Calculating with Functions
This time we want to write calculations using functions and get the results. Let's have a look at some examples:

seven(times(five())); // must return 35
four(plus(nine())); // must return 13
eight(minus(three())); // must return 5
six(dividedBy(two())); // must return 3
Requirements:

There must be a function for each number from 0 ("zero") to 9 ("nine")
There must be a function for each of the following mathematical operations: plus, minus, times, dividedBy (divided_by in Ruby)
Each calculation consist of exactly one operation and two numbers
The most outer function represents the left operand, the most inner function represents the right operand

也就是要求有如下数字函数和操作符函数实现加减乘除运算。

1
2
3
4
5
6
7
8
9
zero()
one()
...
nine()

plus()
minus()
times()
dividedBy()

可以看出,对于数字函数而言,如果没有参数,则需要返回数字即可;如果有操作符函数,则需要进行运算,也就是说运算符函数是需要返回函数,且该函数包含内部数字函数中的参数。

那么这样就符合闭包的概念了:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

闭包的概念出现于60年代,最早实现闭包的程序语言是Scheme。之后,闭包被广泛使用于函数式编程语言如ML语言和LISP。很多命令式程序语言也开始支持闭包。

在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。运行时,一旦外部的 函数被执行,一个闭包就形成了,闭包中包含了内部函数的代码,以及所需外部函数中的变量的引用。其中所引用的变量称作上值(upvalue)。

实现

考虑到seven(times(five()))的实现,数字函数分为有参和无参的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function five(opt) {
if(opt == undefined) {
return 5;
}
return opt(5);
}

function seven(opt) {
if(opt == undefined) {
return 7;
}
return opt(7);
}
// 操作函数返回一个闭包,引用了内部的数字变量,使外部的数字函数能够使用到该数字变量
function times(num1) {
return function(num2) {
return num2*num1;
}
}

最终实现也就很显然了,将数字函数的公共部分提取出来另添加一个函数(该答案为例题答案)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var n = function(digit) {
return function(op) {
return op ? op(digit) : digit;
}
};
var zero = n(0);
var one = n(1);
var two = n(2);
var three = n(3);
var four = n(4);
var five = n(5);
var six = n(6);
var seven = n(7);
var eight = n(8);
var nine = n(9);
function plus(r) { return function(l) { return l + r; }; }
function minus(r) { return function(l) { return l - r; }; }
function times(r) { return function(l) { return l * r; }; }
function dividedBy(r) { return function(l) { return l / r; }; }