1. 程式人生 > >js 函數語言程式設計 淺談

js 函數語言程式設計 淺談

js 函數語言程式設計

函式式的思想, 就是不斷地用已有函式, 來組合出新的函式。

函數語言程式設計具有五個鮮明的特點:

1. 函式是"第一等公民"
指的是函式與其他資料型別一樣,處於平等地位

2. 只用"表示式",不用"語句"
"表示式"(expression)是一個單純的運算過程,總是有返回值;
"語句"(statement)是執行某種操作,沒有返回值。

3. 沒有"副作用"
指的是函式內部與外部互動(最典型的情況,就是修改全域性變數的值),
產生運算以外的其他結果。

4. 不修改狀態
變數往往用來儲存"狀態"(state)。不修改變數,意味著狀態不能儲存在變數中,
函數語言程式設計使用引數儲存狀態

5. 引用透明
指的是函式的執行不依賴於外部變數或"狀態",只依賴於輸入的引數,任何時候只要引數相同,
引用函式所得到的返回值總是相同的

函數語言程式設計的意義:
1. 程式碼簡潔,開發快速
2. 接近自然語言,易於理解
3. 更方便的程式碼管理
4. 易於"併發程式設計"
5. 程式碼的熱升級

---------------------分割線------------------------

在 JavaScript 中,函式本身為一種特殊物件,屬於頂層物件,
不依賴於任何其他的物件而存在,因此可以將函式作為傳出 / 傳入引數,
可以儲存在變數中,可以做一切其他物件可以做的事情。

自呼叫函式(遞迴--自己呼叫自己)實際上是高階函式的一種形式。

函數語言程式設計示例:

// 階乘的一般實現
function factorial(n) {
  if (n == 1) {
    return 1;
  } else {
    return factorial(n - 1) * n;
  }
}

// 階乘的函數語言程式設計風格實現
function mul(a, b){  return a*b; } 
function dec(x){  return x - 1; } 
function equal(a, b){  return a==b; } 

function factorial(n) {
  if (equal(n, 1)) {
    return 1;
  } else {
    return mul(n, factorial(dec(n)));
  }
}
---------------------分割線------------------------
函式柯里化:
是把接受多個引數的函式變換成接受一個單一引數(最初函式的第一個引數)的函式,
並且返回接受餘下的引數而且返回結果的新函式的技術。

我的理解就是需要反覆呼叫一個方法a(),它的引數中有一個引數在一定的狀況下的值不易改變,
每次呼叫都寫很多次未免太過麻煩,而且也體現不出來它描述和解決的一類問題,這種情況下,
為了避免這兩個問題,我們就將其中那個不容易改變的引數給固定下來,新生成一個函式來應對
這一類問題。
加法柯里化的簡單實現:
//加法
function adder(num) {
    return function(x) {
      return num + x;
    }
}

var add5 = adder(5);
var add6 = adder(6);

console.log(add5(1));// 6
console.log(add6(1));// 7
這裡的add5表示的函式可以解決基數為5的加法問題,只需要傳遞一個引數就可以了,
而不必像普通的加法函式那樣每次呼叫都必須新增兩個引數。
//計算m的n次方
var powerOfN = function(n){
    return function(m){
        var res = 1;
        for(var i = 0; i < n; ++i){
            res *= m;
        }
        return res;
    } ;
};
//按需生成
var powerOf2 = powerOfN(2);
var powerOf3 = powerOfN(3);
//呼叫傳參
console.log(powerOf2(3));
console.log(powerOf3(2));
柯里化通用實現:
function curry(fn) {
    var args = [].slice.call(arguments, 1);
    return function() {
        var inargs = [].slice.call(arguments);
        console.log(args.concat(inargs));
        return fn.apply(null, args.concat(inargs));
    }
}

function curry(fn) {
    var args = [].slice.call(arguments, 1);
    return function() {
        var inargs = [].slice.call(arguments);
        console.log(args.concat(inargs));
        return fn.apply(null, args.concat(inargs));
    }
}
function add(num1, num2) {
    return num1 + num2;
}
var newAdd = curry(add, 5);
console.log(newAdd(6));
柯里化將降低了函式使用的普遍性,增加了使用的特異性,使用柯里化需要認真識別
其使用的場景,在符合要求的地方使用,不然會顯得囉嗦,降低程式碼的可讀性。
---------------------分割線------------------------
高階函式
高階函式即為對函式的進一步抽象,就是以其它函式為輸入,或者返回一個函式為輸出的函式。
高階函式最常見的應用如 map(對映), reduce(規約), forEach(遍歷), filter(過濾)等,
它們都是以傳入不同的函式來以不同的方式運算元組元。
簡單應用:
function foo(f, g) {
  return function() {
    return f.call(null, g.apply(null, arguments));
    //return f(g.apply(null, arguments)); 也是可以的
  }
}
var sum = function(x, y) {
  return x + y;
}
var square = function(x) {
  return x * x;
}
var squareofsum = foo(square, sum);
squareofsum(2, 3);// 25
下面我們來看一下怎樣從程序式程式設計過渡到函數語言程式設計的:

1>形式一

var sum = function(x, y) {
  return x + y;
}
var square = function(x) {
  return x * x;
}
function foo(){
  return square( sum(2, 3) );
}
foo();// 25
2>形式二
var sum = function(x, y) {
  return x + y;
}
var square = function(x) {
  return x * x;
}
function foo(f, g){
  return f( g(2, 3) );
}
foo(square, sum);// 25
3>形式三
var sum = function(x, y) {
  return x + y;
}
var square = function(x) {
  return x * x;
}
function foo(f, g){
  return function(){
    var num1 = arguments[0];
    var num2 = arguments[1];
    console.log(num1, num2);// 2 3
    var temp = g(num1, num2);
    console.log(temp);// 5
    return f(temp);
  };
}
var myfunc = foo(square, sum);
myfunc(2, 3);// 25
4>形式四
var sum = function(x, y) {
  return x + y;
}
var square = function(x) {
  return x * x;
}
function foo(f, g){
  return function(){
    var temp = g.apply(null, arguments);
    return f(temp);
  };
}
var myfunc = foo(square, sum);
myfunc(2, 3);// 25
最後的形式四就是我們想要得到的效果。

---------------------分割線------------------------
其他示例:

1>

function foo(fn, array, value){
  var res = array.map(function(ele){
    return fn.apply(null, [ele].concat(value));
  });
  return res;
}

function add(x, y) {
  return x + y;
}

function sub(x, y) {
  return x - y;
}

console.log( foo(add, [1, 2, 3], 3) );// [4, 5, 6]
console.log( foo(sub, [1, 2, 3], 3) );// [-2, -1, 0]

2>
function multicast(fn) {
  return function (){
    var pre = arguments[0];
    var rest = arguments[1];
    var ret = pre.map(function(ele) {
      return fn.apply(this, [ele].concat(rest));
    });
    return ret;
  }
}

function add(x, y) {
  return x + y;
}
var newAdd = multicast(add);
console.log(newAdd([1,2,3],3));// [4, 5, 6]

function sub(x, y) {
  return x - y;
}
var newSub = multicast(sub);
console.log(newSub([1,2,3],3));// [-2, -1, 0]

參考資料:

https://blog.oyanglul.us/javascript/functional-javascript.html

http://www.phodal.com/blog/javascript-higher-order-functions/

http://www.cnblogs.com/wing011203/archive/2013/07/07/3176641.html

http://www.ibm.com/developerworks/cn/web/1006_qiujt_jsfunctional/

http://www.cnblogs.com/pigtail/p/3447660.html

http://www.ruanyifeng.com/blog/2012/04/functional_programming.html