是时候介绍如何在F#中定义函数了,在你没有接触过函数式编程语言之前,你也许会觉得C#/Java的语法已经够丰富了,有什么任务做不了呢?当你读过函数式编程之Currying函数式编程之Partial application,你就会发现C#在函数式编程方面已经略显无力了,虽然我用C#模拟了这两种函数式特性,但是实现价值已经不大了,也许你从来没见到过有人这样用C#,因为对于C#而言就是OO范式,只是借鉴了少数函数式特性而已,比如LINQ。

当然写这篇博客的目的除了让西安的.NET社区发展壮大,让更多的人加入我们,一起参加线下分享,对我自己而言我是有私心的,那就是把整个函数式编程思想捋一遍,如果我能讲明白说明我是理解的,对不明白的地方还会继续查阅书籍。

另外写博客的好处还在于我可以畅所欲言,其实我作为过来人很想给年轻人一些建议和指引。当大家都在热衷于学习各类关于分布式架构微服务的时候我其实是想告诉他们静下心来,特别当你还是一个经验不算丰富的开发者。作为一个还算对微服务/DDD/CQRS略有经验的人,我是不会轻易提出这些方案的。除非我的客户和交付团队具备了这样的能力,我才会安心的增加这种复杂度。我也会在未来写出诸如软件架构的十种风格之类的连载,但是当大家没搞明白这些架构风格的来龙去脉之前把时间花在上面会走很多弯路,有时候你觉得高不可攀的东西早在几十年前就有了,只是现在换个名字,换个框架而已。

反之我们在看了很多三五年,甚至是十年以上的来自候选人的作业,能够达到标准的寥寥无几。我们在考察候选人的代码风格,对事物的抽象能力。而你的代码随意命名,没有使用语言的任何新特性,没有关键的业务抽象,更不知道什么是SOLID 原则, 你跟我谈DDD?怎么可能。

定义函数

言归正传,看看用C#如何实现求平方和的功能:

public int SumOfSquare(int n)
{
var sum = Enumerable.Range(1, n)
.Select(x => x * x)
.Sum(); return sum;
} // 调用
var result = SumOfSquare(100)

这是一段用LINQ编写的声明式风格的代码,相比for loop而言,这段代码已经非常优雅了。

在F#是如何实现的呢?

// 定义一个求平方的函数square
let square x = x * x // 定义sumOfSquares函数
let sumOfSquares n =
[1..n]
|> List.map square
|> List.sum // 调用
sumOfSquares 100

符号|>被称作是管道符,它的作用是把第一个表达式的输出放入第二个表达式的输入,以此类推将一段函数连接起来。

上面的代码可以描述为:

  • 先定义一个求平方的函数sqaure
  • 把[1..n]生成的list放入List.map函数中,List.map的定义为:
('a -> 'b) -> 'a list -> 'b list

即接受一个'a -> 'b类型的任意函数,同时接受'a类型的list数据,最终返回`b类型的list数据,跟C#中的Select函数差不多。

  • 把List.map函数返回的数据放入 List.sum函数中

由于type inference的原因,F#几乎不用声明类型,另外也没有大括号和分号用来表示语句终止。F#采用了和Python类似的思路,用缩进来表示不同的作用域。

理解管道符 |>

上面的例子使用到了管道符|>,其实它并不是什么神秘的东西,他也是一个函数,只不过函数名就叫|>而已。

F#通过下面的代码来定义|>:

let (|>) x f = f x

用()括起来的函数名可以理解为运算符,例如+-,运算符是支持中缀表达式的,例如你可以这样使用:

2 |> string

其中2就是管道符定义中的x,string函数就是管道符定义中的f,最终等价于:

string 2

再来一个例子:

let add x y = x + y

2 |> add 3

其实就是:

add 3 2

定义能够做Partial application的函数

Partial application是指在调用函数式只提供部分参数,从而生成一个嵌入已知参数的新函数,这里面的关键在于函数的参数顺序。

let addWithPluggableLogger logger x y =
let result = x + y
logger "x+y" result
result

例如我们定义了一个具有logging能力的add函数,参数logger作为一个相对稳定的参数被设计到了第一个位置,比如你可以用consoleLogger做Partial application:

let addWithConsoleLogger = addWithPluggableLogger consoleLogger
addWithConsoleLogger 1 2

试想如果参数logger被设计到了其他的位置,可能就无法定义出addWithConsoleLogger这样的函数了。

那么到底如何设计函数的参数顺序呢?下面的指导原则可以供参考:

  1. 对于稳定的参数要设计在前面,因为你最有可能对相对稳定/固定的参数做Partial application
  2. 对数据类的参数放在后面,例如List库的大多函数List.mapList.minBy都是这样设计的:
List.map (fun i -> i + 1) [0;1;2;3]
List.minBy (fun i -> i + 1) [0;1;2;3]

基于这样的函数定义,你才可以通过管道符把对一个数据集合的操作连接起来:

let result =
[1..10]
|> List.map (fun i -> i+1)
|> List.filter (fun i -> i>10)

另外这样定义的函数还特别容易粘接和组合,下一篇将介绍如何组合函数。

函数式编程之-定义能够支持Partial application的函数的更多相关文章

  1. 函数式编程之-Partial application

    上一篇关于Currying的介绍,我们提到F#是如何做Currying变换的: let addWithThreeParameters x y z = x + y + z let intermediat ...

  2. Python3 函数式编程

    函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用.而允许使用变量的程序设计语言,由 ...

  3. Python进阶之函数式编程

    函数式编程 函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计 ...

  4. Scala函数与函数式编程

    函数是scala的重要组成部分, 本文将探讨scala中函数的应用. scala作为支持函数式编程的语言, scala可以将函数作为对象即所谓"函数是一等公民". 函数定义 sca ...

  5. Python进阶:函数式编程(高阶函数,map,reduce,filter,sorted,返回函数,匿名函数,偏函数)...啊啊啊

    函数式编程 函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计 ...

  6. 【Python】【函数式编程】

    #[练习] 请定义一个函数quadratic(a, b, c),接收3个参数,返回一元二次方程: ax2 + bx + c = 0 的两个解. 提示:计算平方根可以调用math.sqrt()函数: & ...

  7. Python学习--08函数式编程

    函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数. 高阶函数 Python支持高阶函数(Higher-order function). 什么是高阶函数呢?把函数作为参 ...

  8. (转)Python进阶:函数式编程(高阶函数,map,reduce,filter,sorted,返回函数,匿名函数,偏函数)

    原文:https://www.cnblogs.com/chenwolong/p/reduce.html 函数式编程 函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数 ...

  9. python笔记三:函数式编程

    1.概念: 函数式编程就是一种抽象程度很http://i.cnblogs.com/EditPosts.aspx?opt=1高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要 ...

随机推荐

  1. oracle中向timeStamp类型字段插入当前时间

    to_timestamp(to_char(sysdate,'YYYY-MM-DD HH24:MI:SS'),'YYYY-MM-DD HH24:MI:SS')

  2. tomcat与iis公用80端口(已经发布.net项目现在开发Java项目时tomcat在eclipse中localhost:8080打不开问题)

    在开发过.net项目的电脑上安装eclipse配置tomcat运行时打不开页面问题描述,这也是本人亲生经历,找了好多资料网上大多都是tomcat配置问题描述,今天突然想到是不是IIS的问题,果然上网一 ...

  3. 86、UIWindow简单介绍

    一.介绍 UIWindow是一种特殊的UIView,通常在一个app中只会有一个UIWindow ios程序启动完毕后,创建的第一个视图控制器 ,接着创建控制器的view,最后将控制器的view添加到 ...

  4. [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.2:compile (default-compile) on project taotao-manager-pojo: Compilation failure

    运行maven项目时报错 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.2:compi ...

  5. MyBatis generator配置 overwrite 文件覆盖失效

    工具:IDEA.jdk1.8.mysql 底部有解决方法! pom.xml配置 <plugins> <!--Mybatis自动代码插入--> <plugin> &l ...

  6. C#项目学习记录

    1,   Visual Studio Code 添加VS 2017的开发人员命令提示符---C#编译环境 2,  C#编译器和CLI的安装 注意:自己的电脑上配置环境变量时,配置在系统变量的Path中 ...

  7. Day10 (黑客成长日记) Urllib库的使用

    什么是Urllib: Urllib是python内置的HTTP请求库包括以下模块urllib.request 请求模块urllib.error 异常处理模块urllib.parse url解析模块ur ...

  8. PC装MAC(VM虚拟机)想体验苹果系统的福利

    Windows下 VM12虚拟机安装OS X 10.11(详细教程) 工具/原料 Mac OS X 10.11 镜像文件 unlocker208文件 VMware Workstation12(版本不一 ...

  9. 玩玩微信公众号Java版之七:自定义微信分享

    前面已经学会了微信网页授权,现在微信网页的功能也可以开展起来啦! 首先,我们先来学习一下分享,如何在自己的页面获取分享接口及让小伙伴来分享呢? 今天的主人公: 微信 JS-SDK, 对应官方链接为:微 ...

  10. Ubuntu英文变为中文

    Ubuntu英文变为中文 注:(我也是第一次变语言,写的有点乱,我把重点的用红字表示.)     1.点击这个软件更新.  2.只有点击了上面那一步,这里才会软件资源 Software Sources ...