函数式编程之-Currying
这个系列涉及到了F#这门语言,也许有的人觉得这样的语言遥不可及,的确我几乎花了2-3年的时间去了解他;也许有人觉得学习这样的冷门语言没有必要,我也赞同,那么我为什么要花时间去学习呢?作为一门在Tiobe排行榜里50名开外的语言,很显然我学习他并不是为了指着F#混口饭吃,也许有一天我会为了养家糊口转向Java,但在这之前Java并没有任何吸引我学习他的地方。因为从本质上说C#和Java都是同种风格的语言,他两在语言层面的能力几乎是一样的。
如果一种编程语言不能影响你对编程的思考方式,就不值得学习
来自 《Epigrams on Programming》
函数式语言来自数学家之手,天生有点高不可攀,各种稀奇古的名词也会让人望而生畏,但他不一定就比OO范式复杂。我们从一开始接触计算机语言几乎就在学习OO,一般来说没有3-5年的编程经验几乎不会领会OO那一套思想,各种设计模式和原则对于一个经验5年的OO选手都不一定能完全领会。从这个角度讲不见得OO就是比函数式编程思想更加简单的范式。
终于可以引出本文的重头戏Currying,Currying一词源于以为对函数式编程语言有影响的数学家Haskell Curry。Currying讲了一件什么样的事情呢?
在函数式语言中并没有类的概念,很显然类是一个来自OO的概念。函数式编程思想中只有函数,那么一个应用程序全靠函数来堆积现实么?OO通过类的概念封装了细节,通过向外界提供更高层级的抽象来防止程序陷入细节。函数式语言则通过组合函数来提供更高级,更复杂的功能。
大家一定见到过lego积木吧,不小小看lego每一个小的零件,通过组合,你可以堆出一个超乎想象的庞然大物。

整个函数式编程的核心思想就是组合,他描述了如何把各种形形色色的东西例如函数、各种类型组合在一起。
那么问题来了,是不是所有的函数都可以自由组合呢?答案是否定的。以一个对数字的操作为例:

整个过程可以组合为一个函数:

只有函数在拥有一个输入和一个输出的时候才能被组合起来,具体如何组合请关注后面的章节,那么对于拥有两个参数的函数如何组合?答案就是Currying ,通过Currying可以将任意函数生成一个只接受一个参数的新函数。
为什么这样说,请参考下面的两张图,当然具体如何组合函数会在以后的章节详细描述。


下面的函数接受两个参数:
public int AddWithTwoParameters(int x, int y)
{
return x + y;
}
如何把它变成只接受一个参数的函数?
public Func<int, int> AddWithTwoParameters(int x)
{
Func<int, int> subFunction = y => x + y;
return subFunction;
}
经过变形,当你调用AddWithTwoParameters时:
- 调用
AddWithTwoParameters并传入一个参数 AddWithTwoParameters返回了一个新的函数,并把参数x包裹在了里面- 通过传递第二个参数来调用新生成的函数
对比下面这两种函数的使用方式:
// 正常版本的调用
var result = AddWithTwoParameters(10, 2);
// Currying版本的调用
var intermediateFn = AddWithTwoParameters(10);
var result = intermediateFn(2);
Currying 函数的签名
如果用C#中的Func来表示这两种风格的函数,他们会有一点微妙的区别:
- 普通版本的Add函数可以用下面的
Func来表示:
Func<int,int,int>
他表示一个接受两个int值的函数,返回类型为int。
- Curry版本的Add函数可以用下面的
Func来表示:
Func<int,Func<int,int>>
他表示一个接受int参数并返回类型为Func<int,int>的函数。
Currying多个参数的函数
跟两个函数的Currying过程类似,考虑三个参数的函数,正常版本如下:
public int AddWithThreeParameters(int x, int y, int z)
{
return x + y + z;
}
Currying版本:
public Func<int, Func<int, int>> AddWithThreeParameters(int x)
{
Func<int, Func<int, int>> subFunction = y => z => x + y + z;
return subFunction;
}
调用过程:
// 正常版本的调用
var result = AddWithThreeParameters(1, 2, 3);
// Curried的版本调用
var intermediateFn1 = AddWithThreeParameters(1);
var intermediateFn2 = intermediateFn1(2);
var result1 = intermediateFn2(3);
你能够写出四个参数的Currying版本吗?当然可以,同时你也许已经观察到了,Currying版本的函数返回值类型特别繁琐,如果你第一次看到这样的返回类型一定不明白他到底在干嘛,另外手动Currying的过程也很痛苦,能不能自动完成Currying呢?
public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
{
return x => y => func(x, y);
}
public static Func<T1, Func<T2, Func<T3, TResult>>> Curry<T1, T2, T3, TResult>(this Func<T1, T2, T3,TResult> func)
{
return x => y => z=>func(x, y,z);
}
F#如何做Currying
C#完成Currying的过程看起来没有问题,但是其实整个过程已经表现的很繁琐了,长长的方法签名已经让人无法忍受了。对于函数式语言F#而言,Currying这种能力已经深入语言的骨髓了。
let addWithThreeParameters x y z = x + y + z
let intermediateFn1 = addWithThreeParameters 1
let intermediateFn2 = intermediateFn1 2
let result = intermediateFn2 3
addWithThreeParameters是一个接受三个参数的函数,由于type inference的能力,你并不需要声明参数类型addWithThreeParameters 1通过传递一个参数(Partial应用),F#帮你自动返回了一个Currying过的新函数- 以此类推最终得到result的值
由此可见,虽然C#也能面前完成Currying的转换,但是比起F#来,整个过程已经显得特别繁琐,对于F#而言,整个过程几乎是自然发生的。从这里也能看出来函数式语言在设计的时候已经融合了这些基础的能力。
下一篇讲解Parial应用,喜欢的朋友请关注。
函数式编程之-Currying的更多相关文章
- Scala实战高手****第12课:Scala函数式编程进阶(匿名函数、高阶函数、函数类型推断、Currying)与Spark源码鉴赏
/** * 函数式编程进阶: * 1.函数和变量一样作为Scala语言的一等公民,函数可以直接赋值给变量 * 2.函数更常用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称 ...
- C#函数式编程
提起函数式编程,大家一定想到的是语法高度灵活和动态的LISP,Haskell这样古老的函数式语言,往近了说ruby,javascript,F#也是函数式编程的流行语言.然而自从.net支持了lambd ...
- Scala函数式编程进阶
package com.dtspark.scala.basics /** * 函数式编程进阶: * 1,函数和变量一样作为Scala语言的一等公民,函数可以直接赋值给变量: * 2, 函数更长用的方式 ...
- js函数式编程
最近在看朴灵的<深入浅出nodejs>其中讲到函数式编程.理解记录下 高阶函数 比较常见,即将函数作为参数,或是将函数作为返回值得函数. 如ECMAScript5中提供的一些数组方法 fo ...
- (转)现代C++函数式编程
本文转自:http://geek.csdn.net/news/detail/96636 现代C++函数式编程 C++ 函数式编程 pipeline 开发经验 柯里化 阅读2127 作者简 ...
- 用C++进行函数式编程
http://www.programmer.com.cn/12717/ 文 / John Carmack 译 / 王江平 <Quake>作者Carmack认为追求函数式的程序设计有着实 ...
- 转自:Python函数式编程指南(二):函数
2. 从函数开始 2.1. 定义一个函数 如下定义了一个求和函数: 1 2 def add(x, y): return x + y 关于参数和返回值的语法细节可以参考其他文档,这里就略过了. ...
- 可爱的 Python : Python中的函数式编程,第三部分
英文原文:Charming Python: Functional programming in Python, Part 3,翻译:开源中国 摘要: 作者David Mertz在其文章<可爱的 ...
- Scala入门系列(九):函数式编程
引言 Scala是一门既面向对象,又面向过程的语言,Scala的函数式编程,就是Scala面向过程最好的佐证.也真是因此让Scala具备了Java所不具备的更强大的功能和特性. 而之所以Scala一直 ...
- [搬运] 写给 C# 开发人员的函数式编程
原文地址:http://www.dotnetcurry.com/csharp/1384/functional-programming-fsharp-for-csharp-developers 摘要:作 ...
随机推荐
- Springboot单例模式实战封装json转换
一.定义 保证一个类仅有一个实例,并提供一个全局访问点. 二.优点 (1)在内存里只有一个实例,减少了内存开销 (2)可以避免对资源的多重占用 (3)设置全局访问点,严格控制访问 ...
- Unity3D编辑器扩展(三)——使用GUI绘制窗口
前两篇分别讲解了创建菜单https://www.cnblogs.com/xiaoyulong/p/10115053.html和创建窗口https://www.cnblogs.com/xiaoyulon ...
- Ubuntu 16.04上安装Global阅读源代码工具
参照10年前写的文档 (Linux源码阅读工具lxr和glimpse的安装与配置),想重新搭建一个源代码阅读工具,发现源里面都没有相关的工具了. 然后看到有更简单的安装工具Global可以使用,所以果 ...
- Tensoflow API笔记(N) 设备指定
tf.device是tf.Graph.device()的一个包装,是一个用于指定新创建的操作(operation)的默认设备的环境管理器.参数为device_name_or_function, ...
- python之路(十一)-socke开发
socket简介 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. so ...
- 将表格添加到Word文档中 ,包括表格样式设置
创建 Table 对象并设置其属性 在您将表格插入文档之前,必须创建 Table 对象并设置其属性. 要设置表格的属性,请创建TableProperties对象并为其提供值. TablePropert ...
- POJ1964-City Game
给你N×M大的矩阵,里面分别有字符‘F'和’R',要找到一个最大的只有‘F'的矩阵,不能包含有’R‘.N,M<=1000. 一开始的思路是单调栈来求最大矩形面积,因为没看清题目不能包含’R'字符 ...
- 【原创】岁月如歌 一款网易歌单生成pdf的软件
介绍 这是一款可以将网易云音乐的歌单中所有歌词输出为pdf的软件. 项目持续维护地址 http://brightguo.com/song-list-to-pdf/ 目前没有搜到相关网易歌单导出为pdf ...
- 【repost】javascript callback
在javascript中回调函数非常重要,它们几乎无处不在.像其他更加传统的编程语言都有回调函数概念,但是非常奇怪的是,完完整整谈论回调函数的在线教程比较少,倒是有一堆关于call()和apply() ...
- 转 多租户SaaS架构
当使用Techcello框架开发云端多租户SaaS应用程序时,它继承了经过验证和测试的架构蓝图和工程结构.但开发人员仍然会保留灵活性,自由和控制权,以修改和扩展能力以适应其应用要求.此外,SaaS平台 ...