当前位置:主页 > 达州 >

老子是魔法少女_函数式编程之Currying

这个系列涉及到了F#这门语言,也许有的人觉得这样的语言遥不可及,的确我几乎花了2-3年的时间去了解他;也许有人觉得学习这样的冷门语言没有必要,我也赞同,那么我为什么要花时间去学习呢?作为一门在Tiobe排行榜里50名开外的语言,很显然我学习他并不是为了指着F#混口饭吃,也许有一天我会为了养家糊口转向Java,但在这之前Java并没有任何吸引我学习他的地方。因为从本质上说C#和Java都是同种风格的语言,他两在语言层面的能力几乎是一样的。

如果一种编程语言不能影响你对编程的思考方式,就不值得学习
来自 《Epigrams on Programming》

函数式语言来自数学家之手,天生有点高不可攀,各种稀奇古的名词也会让人望而生畏,但他不一定就比OO范式复杂。我们从一开始接触计算机语言几乎就在学习OO,一般来说没有3-5年的编程经验几乎不会领会OO那一套思想,各种设计模式和原则对于一个经验5年的OO选手都不一定能完全领会。从这个角度讲不见得OO就是比函数式编程思想更加简单的范式。

终于可以引出本文的重头戏CurryingCurrying一词源于以为对函数式编程语言有影响的数学家Haskell CurryCurrying讲了一件什么样的事情呢?

在函数式语言中并没有的概念,很显然是一个来自OO的概念。函数式编程思想中只有函数,那么一个应用程序全靠函数来堆积现实么?OO通过的概念封装了细节,通过向外界提供更高层级的抽象来防止程序陷入细节。函数式语言则通过组合函数来提供更高级,更复杂的功能。
大家一定见到过lego积木吧,不小小看lego每一个小的零件,通过组合,你可以堆出一个超乎想象的庞然大物。
lego

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

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

只有函数在拥有一个输入和一个输出的时候才能被组合起来,具体如何组合请关注后面的章节,那么对于拥有两个参数的函数如何组合?答案就是Currying ,通过Currying可以将任意函数生成一个只接受一个参数的新函数。
下面的函数接受两个参数:

public int AddWithTwoParameters(int x, int y)
{
    return x + y;
}

如何把它变成只接受一个参数的函数?

public Func AddWithTwoParameters(int x)
{
    Func 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来表示这两种风格的函数,他们会有一点微妙的区别:

diao yong AddWithTwoParameters bing chuan ru yi ge can shu AddWithTwoParameters fan hui le yi ge xin de han shu, bing ba can shu x bao guo zai le li mian tong guo chuan di di er ge can shu lai diao yong xin sheng cheng de han shu dui bi xia mian zhe liang zhong han shu de shi yong fang shi: zheng chang ban ben de diao yong var result AddWithTwoParameters 10, 2 Currying ban ben de diao yong var intermediateFn AddWithTwoParameters 10 var result intermediateFn 2 Currying han shu de qian ming ru guo yong C zhong de Func lai biao shi zhe liang zhong feng ge de han shu, ta men hui you yi dian wei miao de qu bie:

  • 普通版本的Add函数可以用下面的Func来表示:
Func

他表示一个接受两个int值的函数,返回类型为int

  • Curry版本的Add函数可以用下面的Func来表示:
Func<>>

他表示一个接受int参数并返回类型为Func的函数。

Currying多个参数的函数

跟两个函数的Currying过程类似,考虑三个参数的函数,正常版本如下:

public int AddWithThreeParameters(int x, int y, int z)
{
    return x + y + z;
}

Currying版本:

public Func> AddWithThreeParameters(int x)
{
    Func> 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> Curry(this Func func)
{
    return x => y => func(x, y);
}

public static Func>> Curry(this Func 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应用,喜欢的朋友请关注。

当前文章:http://www.honghiem.com/8eg7n/200981-947559-91512.html

发布时间:02:30:54

今期香港挂牌正版彩图2017??六合宝典www.9243.com??藏宝图论坛??香港马报资料??875535.com??www.2235444.com??六合乐坊心水论坛??www.46336.com??六合??今晚大乐透9点开奖号码??

Copyright @ 2016-2017 蜘蛛日博365bet备用网址_365bet平台开户_365bet官网资讯 版权所有