新的编程语言层出不穷,老的快速演进;有的语言追求纯粹,有的以解决工程问题为主;有的语法追求人性化,有的为机器而生;有些语言特性的变化润物无声,有些翻天覆地。要解决上述问题,尝试 回答以下命题:
-
该特性有哪些特点?
-
该特性的“母语”是什么?“母语”会成为其他语言实现的参照或模版。通过学习母语加深理解。
-
该特性有哪些理论基础?理论都很抽象,不易快速掌握,如能了解其核心思想会让对语言特性的理解变得自然顺畅。
-
该特性在其他语言中是如何实现的?各种原因常常会导致一种特性在不同语言中的实现存在很大的差别,有些语言通过语法糖来实现,有些在编译器、解释器上实现,有些通过SDK来实现。理解这些差异能避免踩到雷,减少不必要的BUG,保证程序的性能和健壮性。
拿Functional Programming来讲。函数式编程(FP,functional programming)作为一种编程范式,其很多特性已经被引入到各种语言中,包括静态和动态类型的语言。例如:C++、Java、Scala,Go以及Javascript、Perl、Python等。Java在1.8版本中支持了FP的很多特性。
FP的特点如下:
-
FP强调如何描述问题,剩下的交给计算机来做;IP强调如何让计算机一步一步地执行输入的指令来解决问题。
-
函数是“一等公民”。函数像参数一样传入、返回,习惯了命令式编程,需要改变已有的思维习惯。
-
函数都是无状态的。相同的输入必定产生相同的输出,除了入参,函数的结果不会受Context的影响。
-
高阶函数。一个或多个函数作为入参,或返回结果是一个函数的函数。
-
无副作用。函数不会改变Context。
-
函数组合。函数之间可以组合完成一个功能。
-
惰性求值。
-
异常被视作运算结果的一个特殊值进行处理。
FP的“母语”:Haskell。尽管Haskell不是最早出现的FP语言,但它是目前支持FP特性最全面,其他语言实现FP特性的参照语言之一。
FP的理论基础:范畴论(Category theory)。
范畴是比集合更复杂的数学工具,范畴由三部分组成:
-
一组对象
-
一组态射(morphisms)。每个态射会绑定两个对象,假如f是从源对象A到目标对象B的态射,记作:f:A -> B
-
态射组合。假如h是态射f和g的组合,记作:h = g o f
范畴与集合相比,后者不去研究元素之间的关系,范畴核心就是态射,也就是对象之间的关系,而且对象本身也可以是态射。要搞清楚范畴理论,前提需要熟悉抽象代数,群论等理论,非一早一夕能掌握,理解范畴论的思想,核心的概念也就达到了学习的目的。
事实上,Haskell中的类型和函数构成一个范畴Hask,Haskell中的类型是Hask范畴的对象,Haskell中的函数是Hask的态射。以范畴论中的函子来讲,它对应Haskell中的Functor,其定义如下:
class Functor (f :: * -> *) where fmap :: (a -> b) -> f a -> f b
class表示Functor是一个type class,可以理解为一组方法的集合;class的实例就是Haskell中的类型,例如:Int, Char,[]。f为Hask中的态射,从一种类型指向另外一种类型。Functor包括一个方法fmap,它输入一个函数:a->b,一个f a,输出f b。范畴论中,Functor对应函子,它是两个范畴之间的态射,即:F:C->D,其中C,D是两个范畴。f是函子中作用于对象上的部分,fmap是是函子中作用于态射上的部分。例如下图中(+1)表示元素+1的函数,即a->b,f为[]类型,这里把整数态射为列表,执行的结果就是原列表元素加1得到一个新列表。
ghci>fmap (+1) [0..9][1,2,3,4,5,6,7,8,9,10]
Haskell快排示例:
--Quick sortquickSort :: (Ord a) => [a] -> [a]quickSort [] = []quickSort (x:xs)= let smallerSort = quickSort [a | a <- xs, a <= x] biggerSort = quickSort [a | a <- xs, a > x] in smallerSort ++ [x] ++ biggerSort
x:xs是待排序的列表,这里选取首元素作为排序的标杆,smallSort标示小于等于x的元素,biggerSort代表大于x的元素,递归下去,最终结果就是smallSort+x+biggerSort的列表。
Java的FP例子:
import java.math.BigDecimal;import java.util.function.*;public class TestFunction { public static void main(String[] args) { /** * test function and functions composition. */ Function<BigDecimal, Long> multInt1 = TestFunction::multIntMethod; Function<Long, Long> multInt2 = returnInnerClassWithState(classStateLong); Function<Integer, Long> multInt = returnLambda(); Function<Long, BigDecimal> multLong = (input) -> BigDecimal.valueOf(input * Integer .MAX_VALUE); System.out.println(multLong.compose(multInt).andThen(multInt1).andThen(multInt2).apply (100)); multLong.compose(multInt).apply(1); Predicate<? super Number> ps = (num) -> num.intValue() >= 2; System.out.println("Predicate<? super Number>: " + ps.test(1)); System.out.println("Predicate<? super Number>: " + ps.test(2L)); Predicate<? extends String> pe = (str) -> str.startsWith("a"); //System.out.println("Predicate<? extends Number>: " + pe.test(null)); //compile failed //System.out.println("Predicate<? extends Number>: " + pe.test("abc"); Function<String, Long> innerFun = (intVar) -> new Long(intVar + 1); Function<Number, String> outterFun = (longVar) -> Long.toString(longVar.longValue() + 1); String resStr = outterFun.compose(innerFun).apply("1"); System.out.println("resStr: " + resStr); TestFunction mainTest = new TestFunction(); Long res = multInt2.compose(mainTest.objectFun()).andThen(mainTest::zeroForEver).apply ("123"); System.out.println("res:" + res); BiFunction<Long, Long, Long> sumLong = (f1, f2) -> f1 + f2; System.out.println(sumLong.apply(4L, 5L)); Consumer<Integer> integerConsumer = (i) -> System.out.println("integerConsumer:" + i); Consumer<Number> numberConsumer = (num) -> System.out.println("numberConsumer:" + num .intValue()); Consumer<Object> objConsumer = (obj) -> System.out.println("objConsumer:" + obj); integerConsumer.andThen(numberConsumer).andThen(objConsumer).accept(222); Consumer<Integer> integerConsumer2 = (i) -> System.out.println("integerConsumer2:" + i); integerConsumer.andThen(integerConsumer2).accept(333); Supplier<? extends Number> supplier = () -> new Integer(1); System.out.println("Supplier<? extends Number>: " + supplier.get() + ", class: " + supplier.get().getClass().getName()); Supplier<? super Number> supplier1 = () -> new Long(2); System.out.println("Supplier<? super Number>: " + supplier1.get() + ", class: " + supplier1.get().getClass().getName()); supplier = () -> new Long(1); System.out.println("Supplier<? extends Number>: " + supplier.get() + ", class: " + supplier.get().getClass().getName()); UnaryOperator<String> unaryOperator = (str) -> str + "tttt"; System.out.println(unaryOperator.apply("abc")); Function<String, String> biOperator = Function.identity(); System.out.println(biOperator.apply("abc")); UnaryOperator.identity().apply("abc"); Function.identity().apply("abc"); } private static Long classStateLong = 2L; public static Long multIntMethod(BigDecimal input) { final Integer factor = -1; Long res = Long.valueOf(input.longValue() * factor * classStateLong); System.out.println("multIntMethod, input:" + input.longValue() + ";res:" + res); return res; } private static Function<Integer, Long> returnInnerClass() { return new Function<Integer, Long>() { @Override public Long apply(Integer integer) { return Long.valueOf(integer * -1); } }; } private static Function<Long, Long> returnInnerClassWithState(Long state) { return new Function<Long, Long>() { @Override public Long apply(Long integer) { return Long.valueOf(integer * -1 * state); } }; } private static Function<Integer, Long> returnLambda() { return (i) -> { Long res = Long.valueOf(i * -1); System.out.println("returnLambda, i:" + i + "; res:" + res); return res; }; } public Function<String, Long> objectFun() { return new Function<String, Long>() { @Override public Long apply(String s) { return Long.parseLong(s); } }; } public Long zeroForEver(Long input) { return input * 0; }}