Scala学习笔记(1) -- 为什么要学函数式编程

很久之前我就学过Lisp和Erlang,但是也就是写写HelloWorld,写个排序算法。也在Coursera上听过Scala的课,可是那时候我还不怎么用Java,所以后来也没怎么继续。可是对函数式编程的兴趣一直不减,工作中几乎不会用Scala,但是用的是Java,我一直在想着怎么把Scala用到工作中。最近在写一个工具,因为这个工具基本只有我们项目组用,而且很简单,所以我就用Scala写了。以后有机会,把Scala用在生产上。

为什么对函数式编程这么感兴趣呢。

第一个原因,可以装逼,或者说是个人兴趣。函数式编程给我们提供了另一种思考问题的方法,最重要的是学习这种思维方法。

第二个原因,好多人都在学,好多地方都在用。Java 8增加了lambda表达式的支持,连C++都已经支持lambda表达式和闭包。 有越来越多的用函数式编程语言写的开源软件在被广泛使用,最有名的,用Scala写的Spark,Erlang写的RabbitMQ,当然还有用Lisp写的Emacs。函数式编程语言已经出现四十多年了,为什么现在才火起来,是因为函数式编程语言天然的适合多核编程,或者分布式编程。Google的MapReduce框架就是受到函数式编程的启发而出现的,其实Google的MapReduce和函数式编程的map reduce本质上是一样的。

函数式编程的科学解释



下面是来自知乎的一段回答,对函数式编程做了比较好的解释。

编程范式

函数式编程是一种编程范式,我们常见的编程范式有命令式编程(Imperative programming),函数式编程,逻辑式编程,常见的面向对象编程是也是一种命令式编程。

命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一个冯诺依曼机的指令序列。

而函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式。

函数式编程的本质

函数式编程中的函数这个术语不是指计算机中的函数(实际上是Subroutine),而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的。

在函数式语言中,函数作为一等公民,可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合。

纯函数式编程语言中的变量也不是命令式编程语言中的变量,即存储状态的单元,而是代数中的变量,即一个值的名称。变量的值是不可变的(immutable),也就是说不允许像命令式编程语言中那样多次给一个变量赋值。比如说在命令式编程语言我们写“x = x + 1”,这依赖可变状态的事实,拿给程序员看说是对的,但拿给数学家看,却被认为这个等式为假。

函数式语言的如条件语句,循环语句也不是命令式编程语言中的控制语句,而是函数的语法糖,比如在Scala语言中,if else不是语句而是三元运算符,是有返回值的。

严格意义上的函数式编程意味着不使用可变的变量,赋值,循环和其他命令式控制结构进行编程。

从理论上说,函数式语言也不是通过冯诺伊曼体系结构的机器上运行的,而是通过λ演算来运行的,就是通过变量替换的方式进行,变量替换为其值或表达式,函数也替换为其表达式,并根据运算符进行计算。λ演算是图灵完全(Turing completeness)的,但是大多数情况,函数式程序还是被编译成(冯诺依曼机的)机器语言的指令执行的。

一个直观的例子



在函数式编程里,function is first-class
citizen。在函数式语言的代码里,你会发现各种参数都是函数,这与C语言或者JAVA完全不一样。

有这样一个例子,写一个程序对一组二维数据进行拟合。假设我有下面这些数据:

x :  [1,2,3,4,5]

y:  
[1,4,9,16,25]

我要写一个函数,来得到这组数据的函数。注意,这时候我们要的结果不再是数据,而是函数。

假设我们要写的函数可以自己学习,每次试一个函数,不行,则继续修正(手工修正)。

C语言版本:

int main() {
    int x[5] = {1,2,3,4,5};
    int y[5] = {1,4,9,16,25};
    int a1,a2,a3;
    while(1)
    {
        scanf("%d %d %d",
            &a1,&a2,&a3);
        try(5,x,y,a1,a2,a3);
    }
}
int fun(int x,int a1,int a2,int a3) {
    return a1*x*x+a2*x+a3;
}        
void try(int len,int *x,int *y,
         int a1,int a2,int a3) {
    int i;
    for(i = 0; i < len; i++) {
        if(y[i] != fun(x[i],a1,a2,a3) ) {
            printf("not match\n");
            return;
        }    
    }
    printf("perfect match\n");
}

每次尝试后,就修改一下,最终得到一个a1*x*x+a2*x+a3的函数,实际上是一个系数。

Python版本:

def TRY(X,Y,func):
    i = 0
    while i < len(X):
        if func(X[i])!=Y[i]:
            print "not match"
            return
        i=i+1
    print "perfect match"
 
X=[1,2,3,4,5]
Y=[1,4,9,16,25]
while 1:
    func = raw_input()
    TRY(X,Y,eval(func))
 
[email protected]:~> python find.py
lambda x : x*x+5
not match
lambda x : x*x*x*x*x*x +1
not match
lambda x : x * x
perfect match

上面的两个程序的不同的地方在于,C语言版的函数是定死的,而Python版的函数可以作为变量输入。在C语言中,变量就是变量,函数就是函数(函数指针指针虽然可以作为参数传给另一个函数,但也是静态的,而且这里的函数更应该叫做routine)。

我们把这个例子扩展一下,假设有海量的数据,我们要用机器学习的方法得到这些数据中的一些规律,这些规律就是函数,而机器就是不断产生新的函数应用于这些数据。试想一下,在这种情况下,当然是函数式编程更理想。未来的IT系统,都是分布式的,数据都是海量的,传统的锁机制已经不能适应这种分布式编程了,而函数式编程就是一种可以在语言级解决问题的一个方法,当然不是所有问题都能解决。

函数式语言的代码更加简洁易懂



写函数式编程语言时,最难的是转变思想。例如计算一个数组的和,

C语言:

int a[5] = {1,2,3,4,5};
    int sum = 0;
    for( int i = 0;i<5;i++) {
        sum +=a[i];
}

用函数式编程语言写(Python,也可以用上面的for循环,不过那就和用C++写C语言程序一样了):

a = [1,2,3,4,5]
sum = reduce(lambda x,y:x+y, a,0)

这里的reduce的作用是:

有一个累计值acc是0,reduce将acc=acc+y应用到每个a的元素上,最后就得到了a的各个元素的和。

Google的MapReduce就是受函数式编程语言的map和reduce启发而产生的(reduce在有的语言中叫fold,就是把列表折叠一下)。

比如我用python写一个WordCount,给一个字符串数组,返回其中某个单次的个数。

思路:找"a"的个数,将["a","b","c","a"]映射成[1,0,0,1],再求和

#这个函数返回的是一个函数
#判断一个词是不是等于word,如果等于word,返回1,否则返回0
#在函数式编程语言中,到处倒是可以返回函数的函数
def isTargetWord(word):       
    def isWord(w):        
      if w == word:                
        return 1    
    else:       
      return 0
    return lambda x: isWord(x)
       
words = ["fuck","bitch","fuck"]
print "fuck:",reduce(lambda x,y:x+y,map(isTargetWord("fuck"),words),0)
print "bitch:",reduce(lambda x,y:x+y,map(isTargetWord("bitch"),words),0)

python的lambda表达式里不支持if-else,如果用scala

val words = List("fuck","bitch","fuck")
println("fuck:" + words.map( x=>if(x=="fuck") 1 else 0).reduce((x,y)=>x+y))

这种语法,先map,在reduce,看起来比较自然。一但理解了函数式编程语言,那么代码将会变得特别简洁易懂。当然让一个从来没学过函数式编程的人看上面的代码,肯定一头雾水,一但懂了,就觉得写起来特别方便。

总结



总之,对于一个热爱技术的人,一定要学一下函数式编程。

时间: 2024-11-14 18:06:35

Scala学习笔记(1) -- 为什么要学函数式编程的相关文章

Scala学习笔记及与Java不同之处总结-从Java开发者角度

Scala与Java具有很多相似之处,但又有很多不同.这里主要从一个Java开发者的角度,总结在使用Scala的过程中所面临的一些思维转变. 这里仅仅是总结了部分两种语言在开发过程中的不同,以后会陆续更新一些切换后在开发过程中值得注意的地方.以下列举了部分,但令人印象深刻的Scala语言的不同之处,具体的代码演示样例及具体阐述见下文. ? Scala中可直接调用Java代码,与Java无缝连接. 语句能够不用";"结束.且推荐不适用";". 变量声明时以var或va

原创:Scala学习笔记(不断更新)

Scala是一种函数式语言和面向对象语言结合的新语言,本笔记中就零散记下学习scala的一些心得,主要侧重函数式编程方面. 1. 以递归为核心控制结构. 实现循环处理的方式有三种:goto,for/while,递归,其中用goto实现循环已经在现代语言中被放弃,而for/while形式的结构化编程成为主流,而递归作为另一种方案,则长期只流行在函数式编程的小圈子中. 递归被主流编程界所担心的主要是过深的调用栈,甚至以前的课堂上我们还亲自尝试过将递归改写为循环,但是现代函数式编程语言中,通过尾递归(

Scala学习笔记及与Java不同之处总结-从Java开发人员角度

Scala与Java具有许多相似之处,但又有许多不同.这里主要从一个Java开发人员的角度,总结在使用Scala的过程中所面临的一些思维转变.这里只是总结了部分两种语言在开发过程中的不同,以后会陆续更新一些切换后在开发过程中值得注意的地方.下面列举了部分,但令人印象深刻的Scala语言的不同之处,具体的代码示例及详细阐述见下文. ? Scala中可直接调用Java代码,与Java无缝连接: 语句可以不用";"结束,且推荐不适用";": 变量声明时以var或val开头

蛋哥的学习笔记之-基于Unity的Shader编程:X-1 音乐水波特效

蛋哥的学习笔记之-基于Unity的Shader编程:X-1 音乐水波特效 热度 13728 2015-7-11 23:34 |个人分类:蛋哥的学习笔记之-基于Unity的Shader编程| 音乐, Unity, Shader, 水波, Shader, Shader, Shader, Shader 一.要干些啥: 很久很久没有写文档了,前段时间做了个个人很喜欢的,自认为比较原创的小特效,所以写个文档纪念下(本人特别喜欢音乐) 思路其实很简单,首先用顶点着色器实现一般的水波特效,然后解析音频数据(我

Scala学习笔记一之基础语法,条件控制,循环控制,函数,数组,集合

前言:Scala的安装教程:http://www.cnblogs.com/biehongli/p/8065679.html 1:Scala之基础语法学习笔记: 1:声明val变量:可以使用val来声明变量,用来存放表达式的计算结果,但是常量声明后是无法改变它的值的,建议使用val来声明常量: 声明var变量:如果要声明可以改变的引用,可以使用var变量,声明的常量的值可以改变. 3:指定类型:无论声明val变量还是声明var变量.都可以手动指定其类型,如果不指定,scala会自动根据值,进行类型

Scala学习笔记(一)编程基础

强烈推荐参考该课程:http://www.runoob.com/scala/scala-tutorial.html 1.   Scala概述 1.1.  什么是Scala Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性.Scala运行于Java平台(Java虚拟机),并兼容现有的Java程序. 1.2.  为什么要学Scala 1.优雅:这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API是否优雅直接影响用户体验. 2.速度快:Scala

【转】Java8学习笔记(1) -- 从函数式接口说起

http://blog.csdn.net/zxhoo/article/details/38349011 函数式接口 理解Functional Interface(函数式接口,以下简称FI)是学习Java8 Lambda表达式的关键所在,所以放在最开始讨论.FI的定义其实很简单:任何接口,如果只包含唯一一个抽象方法,那么它就是一个FI.为了让编译器帮助我们确保一个接口满足FI的要求(也就是说有且仅有一个抽象方法),Java8提供了@FunctionalInterface注解.举个简单的例子,Run

【慕课网】php工程师学习计划之我的学习笔记——01 入门必学web基础 htmlcss基础课程 篇

为了进一步学习PHP,本周我选定了慕课网的PHP工程师学习计划, 从今天2015-07-06 10:24:47开始从头学习:计划本周尽快学习完成本课程,谨此作为笔记. 有个好的学习计划和思路非常非常重要,非常感谢慕课网提供本套学习计划,希望更多地学习平台能提供像这样全面一条龙学习思路清晰地教程. 计划图:链接 我的学习状况:2015-07-06 10:29:46 开始随记: php工程师学习计划笔记——01 入门必学web基础 htmlcss基础课程 篇 入门篇: text-align:cent

Scala学习笔记-环境搭建以及简单语法

关于环境的搭建,去官网下载JDK8和Scala的IDE就可以了,Scala的IDE是基于Eclipse的. 下面直接上代码: 这是项目目录: A是scala写的: package first import scala.collection.mutable.ListBuffer object A { def main(args: Array[String]) { print("Hello,Scala");//学习程序设计的第一句 println("---");//pr