函数式编程
函数式编程属于声明式编程的一种,将计算描述为数学函数的求值,但是并没有准确的定义,只是一系列理念,并不需要严格准守,可以理解为函数式编程把程序看做是数学函数,输入的是自变量,输出因变量,通过表达式完成计算。现在越来越多的命令式语言支持部分的函数式编程特性,比如java8中的lambda。
lambda表达式
Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数
python中的lambda表达式
foo = [2, 18, 9, 22, 17, 24, 8, 12, 27]
print map(lambda x: x * 2 + 10, foo)
#[14, 46, 28, 54, 44, 58, 26, 34, 64]
javascript
var foo = [2, 18, 9, 22, 17, 24, 8, 12, 27];
foo.map((v) = > {
return x * 2;
})
//[14, 46, 28, 54, 44, 58, 26, 34, 64]
erlang
Foo = [2, 18, 9, 22, 17, 24, 8, 12, 27],
lists:map(fun(X) -> X *2 end,Foo).
java8
List foo = Arrays.asList(2, 18, 9, 22, 17, 24, 8, 12, 27);
foo.stream().map((cost) -> cost * 2);
使用lambda表达式的好处是减少了代码量,而且使程序更加灵活易读,若不使用lambda表达式,对于以上代码,若需要将每个集合里的数变为原来的3倍,那么必须重新编写一套新的代码,使用lambda表达式可以大大增加代码的灵活性。
高阶函数
在函数式编程中,函数作为一等公民,就是说函数的行为和普通变量没有区别,可以作为函数参数进行传递,也可以在函数内部声明一个函数,那么外层的函数就被称作高阶函数。
javascript
function maps(l,cb){
//函数内部定义函数
let func1 = () =>{console.log("function test1")};
function func2(){
console.log("function test2");
}
let temp = [];
for(let i = 0;i<l.length;i++){
temp.push(cb(l[i]));
}
return temp;
}
let l = [1,2,3,4,5];
//函数作为参数传递
let re = maps(l,(v) => {
return v * 2;
})
console.log(re);
// re : [2,4,6,8,10]
go
func maps(l []int,cb func(v int) int){
for i:=0;i<len(l);i++{
l[i] = cb(l[i])
}
}
list := []int{1,2,3,4}
maps(list, func(v int) int {
return v *2
})
// [2,4,6,8]
柯里化
柯里化(英语:Currying),把接受多个参数的函数变换成接受一个单一参数的函数,返回接受余下的参数并且返回结果的新函数。
比如说在javascript中
//不使用柯里化
function add(a,b){
return a+b;
}
add(1,2);
//currying
function add(a){
return (b) =>{
return a+b;
}
}
add(1)(2)
柯里化的好处是减少了函数的参数个数,并且模块化了每步计算,与设计模式中的适配器模式(将一个接口转换为另一个接口)类似,并且柯里化的应用之一"惰性求值"也是函数式编程的一个重要特性
一次赋值
函数式编程要求所有的变量都是常量(这里所用的变量这个词并不准确,只是为了便于理解),erlang是其中的典型语言,虽然许多语言支持部分函数式编程的特性,但是并不要求变量必须是常量。这样的特性提高了编程的复杂度,但是使代码没有副作用,并且带来了很大的一个好处,那就是大大简化了并发编程。就用java来说吧,java中最常用的并发模式是共享内存模型,依赖于线程与锁,若代码编写不当,会发生死锁和竞争条件,并且随着线程数的增加,会占用大量的系统资源。在函数式编程中,因为都是常量,所以根本就不用考虑死锁等情况。为什么说一次赋值提高了编程的复杂度,既然所有变量都是常量,那么我们没办法更改一个变量的值,循环的意义也就不大,所以haskell与erlang中使用递归代替了循环。
命令式语言中遍历一个数组
c语言
int l[5] = {1,2,3,4,5};
int i = 0;
for(;i<5;i++)
{
//some action
}
erlang
loop([]) -> [];
loop([H|T]) ->
% some action with H
loop(T).
test() ->
loop([1,2,3,4,5]).
%在erlang终端中执行test函数
闭包
闭包是指那些能够访问独立(自由)变量的函数 (变量在本地使用,但定义在一个封闭的作用域中)。换句话说,这些函数可以“记忆”它被创建时候的环境。可以看stackoverflow上的大佬怎么回答的。
javascript代码
function sayHello() {
let i = 0;
return () =>{
return i ++;
}
}
let t = sayHello();
console.log(t(),t(),t());//0,1,2
//闭包会保持其对外部变量的引用