站在C#和JS的角度细谈函数式编程与闭包
1.函数式编程是什么?
摘自百度的说法是。函数式编程是种编程典范,它将电脑运算视为函数的计算。函数编程语言最重要的基础是 λ 演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。和过程化编程相比,函数式编程里,函数的计算可随时调用。
举个例子。平时如果我们要对集合中的元素进行筛选的话,每次都要写循环,都写这么一大堆代码,麻烦不?
static void Main(string[] args)
{
List<int> list = new List<int>() { , , , , };
List<int> result1 = new List<int>();
List<int> result2 = new List<int>();
foreach (var item in list)
{
if (item % == )
result1.Add(item);
} foreach (var item in list)
{
if (item > )
result2.Add(item);
}
}
有没办法构造一个通用的方法?这个方法内部去循环我们要筛选的数组。这样调用方只要指定筛选条件即可。
我们仔细观察下,归纳下它们的共同只处。
1.需要循环
这个很简单,想到循环,我们就应该想到IEnumable
2.筛选条件
我们能不能把筛选元素的这个条件看成一个函数?如
public bool Function(object 当前元素){
    //条件
    if(当前元素.xx属性>)//满足条件
              return true;
       else
        return false;
}
看到这个方法是不是会想到Func<object,bool>?
3.返回结果
这个可以看成把满足条件的元素全部加入到一个集合中,返回回去
知道了这3个条件后,我们大概能构造一个这样的东西
static void Main(string[] args)
{
List<int> list = new List<int>() { , , , , };
var result = MyWhere(list, x => x % == );
}
public static IEnumerable<TInput> MyWhere<TInput>(IEnumerable<TInput> inputlist, Func<TInput, bool> func)
{
foreach (TInput item in inputlist)
{
if (func(item))
yield return item;
}
yield break;
}
但我们每次都要传入一个list,感觉好麻烦,是不是能改造成扩展方法?
class Program
{
static void Main(string[] args)
{
List<int> list = new List<int>() { , , , , };
var result = list.MyWhere(x => x % == );
}
} public static class MyLinq
{
public static IEnumerable<TInput> MyWhere<TInput>(this IEnumerable<TInput> inputlist, Func<TInput, bool> func)
{
foreach (TInput item in inputlist)
{
if (func(item))
yield return item;
}
yield break;
}
}
现在是不是爽多了?
我归纳的函数式编程的核心思想是将相同逻辑的部分封装起来。不同逻辑的部分将它封装成一个函数,由调用方来指定这个函数的逻辑
很多人想问了,JAVA为什么没有类似LINQ这类的扩展方法。。
因为JAVA没有委托这东西,它的思想是完全的面向接口。当然你也可以弄个类似LINQ这类的方法,但用起来会非常变态。首先你要实现定义的某个接口,然后再实现接口里定义的Where方法,这个方法里写你的实现(筛选)逻辑。。这谁会用啊?
如果C#没有匿名委托或lambda。。类似LINQ这类的扩展方法用起来也会很不友好,可以说是变态。
你想想 x=>x.age>5 一句话能搞定,硬要你写个方法。。public bool Find(对象 x){ return x.age>5; }
2.LINQ能移植到JS上吗?
答案是肯定的。可以!函数式编程只是一种思想,只要语言支持匿名方法,玩起来都很爽。
JS是支持匿名函数的,而且是弱类型,所以玩起来非常爽了。
一开始我就在想,有没linq to javascript这东西。结果一搜,果然有=。=
     (function () {
            LINQ = function (data) {
                if (!(data instanceof Array) && !(data instanceof $.fn.init))
                    throw "只支持数组或JQUERY对象";
                this.data = data;
            };
            LINQ.prototype = {
                Where: function (func) {
                    var result = new Array();
                    for (var i = 0; i < this.data.length; i++) {
                        var item = this.data[i];
                        if (func(item))
                            result.push(item);
                    }
                    this.data = result;
                    return this;
                },
                Foreach: function (func) {
                    for (var i = 0; i < this.data.length; i++) {
                        var item = this.data[i];
                        func(item);
                    }
                }
            }
        })();
        $(function () {
            var linq = new LINQ([1, 2, 3, 4, 5, 6]);
            var result = linq.Where(function (item) {
                return item >= 4;
            }).Foreach(function (item) {
                console.log(item);
            });
        })
3.JS的闭包是什么?C#有闭包这个概念吗?
摘自百度的说法是。闭包是可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。
这样说大家可能不明白,下面来看看代码。
场景1
javscript
    $(function () {
        var array = [];
        for (var i = 0; i < 10; i++) {
            array.push(function () {
                console.log(i);
            });
        }
        array.forEach(function (item) {
            item();
        });
    })
C#
List<Action> list = new List<Action>();
for (int i = ; i < ; i++)
{
list.Add(() =>
{
Console.WriteLine(i);
});
}
list.ForEach(x => x());
我们函数体内部对函数体外部的变量i产生了依赖。当我们调用Console.WriteLine(i)时,i已经是9了。而且9个委托中依赖的都是同1个实例i。所以打印出来的全部是10。
如果想输出1,2,3,4,5....该怎么改?其实很简单。
场景2
javascript
   $(function () {
        var array = [];
        for (var i = 0; i < 10; i++) {
            var func = function (item) {
                array.push(function () {
                    console.log(item);
                });
            };
            func(i);
        }
        array.forEach(function (item) {
            item();
        });
    })
C#
List<Action> list = new List<Action>();
for (int i = ; i < ; i++)
{
Action<int> temp = (val) =>
{
list.Add(() =>
{
Console.WriteLine(val);
});
};
temp(i);
}
list.ForEach(x => x());
其实当我们执行temp函数的时候。val已经对i进行拷贝了,val跟i已经没半毛钱关系了(因为C#对于值类型的拷贝是深度拷贝(deep coln,引用类型是拷贝引用),val是一个完全新的实例。所以输出的结果可想而知了。1,2,3,4,5....9。如果觉得难理解可以看下面这段。
static List<Action> list = new List<Action>();
static unsafe void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
Show(i);
}
list.ForEach(x => x());
}
public static unsafe void Show(int val)
{ list.Add(() =>
{
Console.WriteLine(val);
});
}
2段代码是等效的。只不过这里把匿名方法换成了有方法名的具体方法。这样看应该就很好理解了吧。
场景3
上面2种情况下,JS和C#执行后的结果完全一样。但是请看下面这种情况。
javascript
     $(function () {
            var array = [];
            for (var i = 0; i < 10; i++) {
                var temp = i;
                array.push(function () {
                    console.log(temp);
                });
            }
            array.forEach(function (item) {
                item();
            });
        })
C#
List<Action> list = new List<Action>();
for (int i = 0; i < 10; i++)
{
var temp = i;
list.Add(() =>
{
Console.WriteLine(temp);
});
}
list.ForEach((x) => x());
C#理所当然的输出1,2,3,4,5,6...9 为什么?上面说过值类型拷贝是深度拷贝。所以这里会有9个temp实例。而Console.WriteLine(temp)里的temp分别依赖这9个实例的。
再看看JS。执行后,神奇的事情发生了。全部是9。当初我心里很纠结,我实在是想不明白为什么。
后来终于找到答案了。因为JS在函数里面没有代码块作用域(原谅我JS真的很菜)。temp表面上是放在for里面,其实是放在for外面。好吧,心里舒坦多了。如果大家还是不明白,请看下面代码。
static unsafe void Main(string[] args)
{
List<Action> list = new List<Action>();
var temp = 0;
for (int i = 0; i < 10; i++)
{
temp = i;
list.Add(() =>
{
Console.WriteLine(temp);
});
}
list.ForEach((x) => x());
}
我们list里的所有委托中的Console.WriteLine(temp)依赖的都是同一个temp实例。
如果还是不懂。。。。请再看下面这2段代码
public class student
{
public string name { get; set; }
} static unsafe void Main(string[] args)
{
List<Action> list = new List<Action>();
student stud = null;
for (int i = ; i < ; i++)
{
stud = new student()
{
name = "学生" + i
};
list.Add(() =>
{
Console.WriteLine(stud.name);
});
}
list.ForEach((x) => x());
}
执行结果

public class student
{
public string name { get; set; }
} static unsafe void Main(string[] args)
{
List<Action> list = new List<Action>();
for (int i = ; i < ; i++)
{
student stud = new student()
{
name = "学生" + i
};
list.Add(() =>
{
Console.WriteLine(stud.name);
});
}
list.ForEach((x) => x());
}
执行结果

首先最重要的是要理解好C#的值类型和引用类型的区别。
闭包跟匿名函数跟函数式编程之间有着细微关系。如果不好好理解,坑的只是自己。其实语言之间都是大同小异。
站在C#和JS的角度细谈函数式编程与闭包的更多相关文章
- (转) 站在C#和JS的角度细谈函数式编程与闭包
		1.函数式编程是什么? 摘自百度的说法是.函数式编程是种编程典范,它将电脑运算视为函数的计算.函数编程语言最重要的基础是 λ 演算(lambda calculus).而且λ演算的函数可以接受函数当作输 ... 
- 如何编写高质量的 JS 函数(3) --函数式编程[理论篇]
		本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/EWSqZuujHIRyx8Eb2SSidQ作者:杨昆 [编写高质量函数系列]中, <如何 ... 
- 如何编写高质量的 JS 函数(4) --函数式编程[实战篇]
		本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/ZoXYbjuezOWgNyJKmSQmTw作者:杨昆 [编写高质量函数系列],往期精彩内容: ... 
- 翻译连载 | 第 10 章:异步的函数式(上)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇
		原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ... 
- JS最新最细面试题
		转之:https://www.jianshu.com/p/f1f39d5b2a2e 1. javascript的typeof返回哪些数据类型. 答案:string,boolean,number,und ... 
- 【JS】394- 简明 JavaScript 函数式编程-入门篇
		转载自公众号"程序员成长指北" 写在开头 本文较长,总共分为三大部分:(对于函数式编程以及其优点有一定理解的童鞋,可以直接从 第二部分 开始阅读) 第一部分:首先会通过实际代码介绍 ... 
- 翻译连载 |《你不知道的JS》姊妹篇  |《JavaScript 轻量级函数式编程》- 引言&前言
		原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 译者团队(排名不分先后):阿希.blueken.brucec ... 
- 翻译连载 | 第 9 章:递归(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇
		原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ... 
- 翻译连载 | 第 10 章:异步的函数式(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇
		原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ... 
随机推荐
- 源码安装zabbix3.2.7时PHP ldap   Warning
			问题如下: 解决方法: 1.首先查看源码安装的php模块中是否有ldap.so [root@nms ldap]# ll /usr/local/php/lib/php/extensions/no-de ... 
- Windows计算下载文件的SHA256 MD5 SHA1
			引用自 http://blog.163.com/licanli2082@126/blog/static/35748686201284611330/ certutil -hashfile yourfil ... 
- Maven一些零散的知识点
			Maven常用命令: 1. 创建Maven的普通java项目: mvn archetype:create -DgroupId=com.yida.framework -DartifactId=hello ... 
- Selenium入门系列3 单个元素的定位方法
			UI自动化首先要识别对象,再操作对象,最后判定实际结果与预期结果是否一致. 这一节学习的是识别单个对象,webdriver提供了8种方式. <a id="idofa" cla ... 
- java 解决 java.lang.Integer cannot be  cast to java.lang.String
			1.在执行代码打印map的value时,提示错误java.lang.Integer cannot be cast to java.lang.String,这个错误很明显是类型转换错误 查看表字段的数 ... 
- 2017.11.14  C语言---指针的学习
			第八章 善于利用指针 (1)指针是什么 1.内存区每一个字节都有一个编号,这就是"地址".地址形象化的被称为"指针".它能通过以它为地址的内存单元.地址指向(* ... 
- ZooKeeper 完全分布式集群环境搭建
			1. 搭建前准备 示例共三台主机,主机IP映射信息如下: 192.168.32.101 s1 192.168.32.102 s2 192.168.32.103 s3 2.下载ZooKeeper, 以 ... 
- DB总结1
			DBA 重构 data new york committee cobol codasyl journal DDL DML 关系演算 域关系演算语言(QBE) 元祖关系演算语言 ... 
- jQuery 效果使用
			.hide() 隐藏匹配的元素. .hide() 这个方法不接受任何参数. .hide([duration][,complete]) duration 一个字符串或者数字决定动画将运行多久. comp ... 
- HashMap的使用
			HashMap的使用 import java.util.HashMap; import java.util.Iterator; //HashMap<key, value>():键值对的形式 ... 
