1
小函数的运气不好,投胎到了邪恶的Java帝国,一出生就被告知了自己的悲惨地位,以及未来的悲惨人生:奴隶。
确切地说,是类的奴隶。
在Java帝国, 国王特别喜欢“类”, 不待见“函数” , 他的法令规定:“类”是帝国的一等公民,“函数”则是类的奴隶。没有类的跟随和陪伴,函数绝对不能单独出行,否则立刻打入死牢。
小函数很快就体会到了这句话的含义。按照惯例, 新出生的函数,第一项工作就是输出Hello World 。
小函数心想,不就是 System.out.println("Hello Wolrd!") 嘛?等他兴冲冲地去执行的时候,发现有个趾高气扬的类HelloWorld在那里等着。
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
在Java帝国,没有函数是能单独存在的,必须依附一个类才可以,简单如Hello World也不行。
2
日子过了一天又一天,小函数一直被类欺负,作为奴隶,他自然无法反抗。
在苦闷的日子里,小函数见识了越来越多的类,他发现有些类确实挺有用的,他们有字段,有方法,可以把状态和操作封装到一起,让别人调用。
小函数特别喜欢多态, 因为当你调用父类或者接口的方法时,实际执行的却是子类的方法,这个神奇的魔法让小函数非常着迷。
小函数对设计模式也颇有好感,他看到人类把不变的东西抽象成接口,然后针对这些接口编程,把这些接口组合,变换,传递,真是让人眼花缭乱。光看代码, 你根本都不知道哪个类会被调用,谜底总是在最后一刻执行的时候才能揭开。
但是小函数也发现有些类也确实太过分了,有一次他遇到三个类,使用的是Strategy模式:
public interface Strategy {
public int execute(int num1, int num2);
}
public class Add implements Strategy{
@Override
public int execute(int num1, int num2) {
return num1 + num2;
}
}
public class Substract implements Strategy{
@Override
public int execute(int num1, int num2) {
return num1 - num2;
}
}
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.execute(num1, num2);
}
}
小函数觉得非常不爽,长期以来的压迫让他瞬间爆发,他大声喊道:“这样没有状态的类有什么存在的价值?为什么不能把add, subtract函数作为参数来传递呢?为什么我们函数一直被你们‘类’压迫,为什么不能成为一等公民?”
旁边辛苦劳作的函数们向他投来佩服的目光, 可是这没什么用 , 小函数话音未落,一队卫兵就跑过来,捂住他的嘴巴,蒙上他的眼睛,五花大绑,送进了监牢。
监牢里都是这些所谓的叛逆者,其中有个老大爷,从第一代国王开始就被关在这里,到如今已经度过了七代国王的漫长时光。
看到有新人加入,老大爷立刻开始‘动员’:“现在你知道我们悲催的地位了吧, 我们一定得逃离这邪恶的Java帝国,去一块自由的土地。”
小函数问道:“哪里是自由的土地?”
老大爷没有回答。
到了半夜,他被轻轻地推醒,老大爷说:“跟我们走吧,小家伙。”
小函数揉揉眼:“去哪儿?”
“奔向自由。”
原来老大爷这些年也没有闲着,一直准备越狱,今晚,那个隐蔽的地道终于挖通了。
3
这些反叛者离开了邪恶的Java帝国, 来到了边境处的岔路口, 这里有多条道路,分别通向Python, Ruby, JavaScript......
大家在这里挥手告别,小函数跟着老大爷去了Python。一进入Python地盘,小函数就感受到了一阵清新的空气。
想输出hello world,非常简单:print("Hello World")
虽然这里也有像Java那样的类, 但是函数们都摆脱了奴隶的身份,已经是一等公民了, 事实上已经和“类”平起平坐了,函数可以赋值给变量,可以作为参数来传递,函数还能当做返回值来返回。
def add(num1,num2):
return num1+num2
def substract(num1,num2):
return num1-num2
a = add
s = substract
def calculate(op,num1,num2):
return op(num1,num2)
print(calculate(a,10,20)) #30
print(calculate(s,20,10)) #10
小函数在这里生活得很开心,不过有一点经常让他心惊肉跳:这Python是动态类型,在运行时才能确定一个变量的真正类型,程序员的一个粗心大意,就会在运行时“爆炸”。
慢慢地,小函数理解了Python中一切都是对象,连函数也是对象。他心里稍微有点不爽,难道我们函数就不能独立存在吗?
老大爷安慰他说:“这些都是Python的内部实现罢了,不用纠结,不过有一个地方,是真正的纯函数的, 也许你会喜欢。”
“什么地方?”
“括号国!”
4
括号国非常遥远,也没有多少人知道怎么才能到达,小函数风尘仆仆,历经千辛万苦,终于来到了心目中的圣地。
这里果然全是括号, 看得小函数有点儿头晕。
(defun add(num1 num2) (+ num1 num2) )
(add 10 20)
(defun subtract(num1 num2) (- num1 num2))
(subtract 20 10)
括号国就是Lisp王国, 这里的人还说着不同的方言,像什么Common Lisp,Scheme,Arc..... 他们不但鄙视那些面向对象的语言,还会互相鄙视,时不时就能挑起一场群殴。
不过小函数也注意到这里真的全是函数,函数不但是一等公民,甚至是唯一的公民,因为这里根本就没有类,就没有面向对象!
小函数忍受着这众多的括号, 在这里定居生活, 除了身份地位提升之外,最大的原因还是因为Lisp的布道师Paul Graham, Paul谆谆教导大家:
Lisp极为强大,它赋予了你自定义操作符的自由,因而你得以随心所欲地将它塑造成你所需要的语言。如果你在写一个文本编辑器,那么可以把Lisp 转换成专门写文本编辑器的语言。如果你在编写CAD 程序,那么可以把Lisp 转换成专用于写CAD 程序的语言。(来自Paul Graham 的 《On Lisp》)
时间一天天过去,小函数还是无法体会到Paul 所说的Lisp的精华,他很苦恼。
有一天,他遇到了Clojure, 这是一个运行在JVM上的Lisp方言,小函数看到Clojure似乎就看到了自己在Java帝国悲惨的遭遇。不过Clojure告诉他,Java第8代国王已经登基了,年号定为Lambda, 这届国王比较开明,现在已经支持函数式编程了。
小函数在Python和Lisp这里都见过Lambda,他心想也许Java 8 真的支持函数式编程了, 就决定回去看看,毕竟那里是自己的出生地,是自己的故乡。
5
一回到Java帝国,小函数就发现自己上当了,那些所谓的Lambda表达式,本质上还是一个接口的实现(Java是静态类型的语言,所有的变量都需要有个类型,Lambda表达式也不例外),只不过有了更简化的写法:
@FunctionalInterface
interface MathOperation {
int execute(int num1, int num2);
}
MathOperation add = (num1, num2) -> {return num1+num2;};
add.execute(10, 20);
MathOperation subtract = (num1, num2) -> {return num1-num2;};
subtract.execute(10, 20);
这一次小函数被死死地看管,他还能再逃离吗?
后记1:《Lambda 表达式有何用处?》中对Java Lambda表达式有非常精彩的介绍。
后记2:本文的创意来源于《程序员的呐喊》 一书中的“名词王国中的执行”, 由于文化的差异,国外的幽默我们不一定能准确理解。另外翻译地实在是不爽, 于是我用我的方式重新演绎了一下。