要解决的问题
如何深入准确地理解并掌握一门高阶语言的特性
新的编程语言层出不穷,老的快速演进;有的语言追求纯粹,有的以解决工程问题为主;有的语法追求人性化,有的为机器而生;有些语言特性的变化润物无声,有些翻天覆地。现实的复杂性使得没有任何一门语言能够满足所有场合的需求,程序员需要准确、深入,快速地掌握一门语言的特性。
解决方案
回答以下问题:
- 该特性有哪些特点?
- 该特性的“母语”是什么?“母语”会成为其他语言实现的参照或模版。通过学习母语加深理解。
- 该特性有哪些理论基础?理论都很抽象,不易快速掌握,如能了解其核心思想会让对语言特性的理解变得自然顺畅。
- 该特性在其他语言中是如何实现的?各种原因常常会导致一种特性在不同语言中的实现存在很大的差别,有些语言通过语法糖来实现,有些在编译器、解释器上实现,有些通过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 sort
quickSort :: (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
--exec on command shell
--fmap (+1) [0..9]
Java函数式编程示例
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;
}
}