<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>函数式编程</title>
</head>
<body>
<h1>js权威指南</h1>
<p>第八章函数__函数式编程示例代码学习</p>
<script>
//8.8.1使用数组处理函数
//计算数组元素的平均值和标准差
//非函数式编程
function fn1(){
var data=[1,1,3,5,5];
var total=0;
for(var i=0;i<data.length;i++){
total+=data[i];
}
var mean=total/data.length;//平均数
console.log(mean);//=>3
//标准差是反映一组数据离散程度最常用的一种量化形式,是表示精确度的重要指标。
//所有数减去其平均值的平方和,所得结果除以该组数之个数(或个数减一,即变异数),再把所得值开根号,所得之数就是这组数据的标准差。
var t=0;
for(var i=0;i<data.length;i++){
var deviation=data[i]-mean;
t+=deviation*deviation;
}
var stddev=Math.sqrt(t/(data.length-1));//标准差
console.log(stddev)//=>2
}
//fn1();
//使用数组方法map()和reduce()计算数组元素的平均值和标准差
function fn2(){
var data=[1,1,3,5,5];
var sum=function(x,y){return x+y};
var square=function(x){return x*x};
//reduce() 使用指定的函数(称为化简函数),将数组元素进行组合,合成单个值返回。
var mean=data.reduce(sum)/data.length;
console.log(mean);//=>3
//map() 从头到尾遍历数组,为每个元素调用指定的函数,指定函数的返回值(必须)组成新的数组。
var deviationArr=data.map(function(x){return mean-x});
var stddev=Math.sqrt(deviationArr.map(square).reduce(sum)/(data.length-1));
console.log(stddev)//=>2
}
//fn2();
//8.8.2 高级函数
function fn3(){
//高阶函数not()返回一个新的函数odd,odd将它的实参传入f(even),odd再返回f(even)的返回值的逻辑非。
function not(f){
return function(){//返回一个新函数
//apply() 允许显示指定调用所需的this值,即,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是这个对象的方法。
//apply()方法要求以数组的形式传入参数。
var result= f.apply(this,arguments);//调用f()
return !result;//对结果求反
}
}
var even=function(x){//判断是否为偶数
return x%2 === 0;
};
var odd=not(even);//判断是否是奇数
var arr=[1,1,3,5,5];
console.log(arr.every(odd));//true
}
//fn3();
//例2
//自定义数组 map() 方法
var map=Array.prototype.map
?function(a,f){return a.map(f)}
:function(a,f){
var results=[];
for(var i= 0,len= a.length;i<len;i++){
//undo:不明白这里为啥需要判断 i in a
if(i in a){
//call()方法允许显示指定调用所需的this值,即,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是这个对象的方法。
//call()方法使用它自有的实参列表作为函数的实参
results[i]= f.call(null,a[i],i,a)
}
}
return results;
};
function fn4(){
//mapper()产生一个新函数,新函数将一个数组映射到另一个使用这个函数的数组上。
function mapper(f){
return function(a){
return map(a,f);
};
}
var increment=function(x){return x+1;};
var incrementer=mapper(increment);
console.log(incrementer([1,2,3]));//=>[2,3,4]
}
//fn4();
//8.8.3不完全函数
//不完全函数是一种函数变换技巧,即把一次完整的函数调用拆分成多次函数调用,每次传入的实参都是完整实参的一部分,每个拆分开的函数叫做不完全函数,每次函数调用叫做不完全调用。
function fn5(){
//实现一个工具函数将类数组对象或对象转为真正的数组
function array(a,n){
//slice() 返回指定数组的一个片段或子数组。含头不含尾,end 可为负。end 省略截取到末尾。
return Array.prototype.slice.call(a,n || 0);
}
function partialLeft(f){
var args=arguments;
return function(){
var a=array(args,1);//开始处理外部第一个args
a= a.concat(array(arguments));//然后增加所有的内部实参
return f.apply(this,a);//然后基于这个实参列表调用f()
}
}
var f=function(x,y,z){return x*(y-z);};
console.log(partialLeft(f,2)(3,4));//=>2*(3-4)=-2
}
//fn5();
//8.8.4记忆
//memorize()接收一个函数作为实参,并返回带有记忆能力的函数。
function memorize(f){
var cache={};//将值保存在闭包里
return function(){
//将实参转换为字符串,并将其用做缓存的键
//join() 将数组中所有元素转为字符串并按指定的分隔符(默认逗号)连接在一起,返回最后生成的字符串。
var key=arguments.length + Array.prototype.join.call(arguments,",");
if(key in cache){
return cache[key];
}else{
return cache[key] = f.apply(this,arguments)
}
};
}
function fn6(){
//定义一个阶乘函数,将上次的计算结果缓存。
//注意:当我们写一个递归函数时,往往需要实现记忆功能。我们更希望调用实现了记忆功能的递归函数,而不是原递归函数。
var factorial=memorize(function(n){
return (n <= 1) ? 1 : n * factorial(n-1);
});
console.log(factorial(5));//=>5*4*3*2*1=120
}
fn6();
</script>
</body>
</html>