主要来自 Scala 语言发明人 Martin Odersky 教授的 Coursera 课程 《Functional Programming Principles in Scala》

------------------------------部分一---------------------------------------

很久以前写过一个非常简单的 python lambda 函数博客,里头有 filter,map,reduce等,是python中很有意思的部分,可以先看看。

另外酷壳网陈皓写了一篇介绍函数式编程的博客,言简意赅,对理解函数式编程很有帮助,非常值得一看。


Scala 本意是可伸展。它的设计哲学是:允许用户通过定义感觉像原生语言支持一样的易用库去在他们需要的方向上改进和发展语言——Scala allows users to grow and adapt the language in the directions they need by defining easy-to-use libraries that feel like native language support.。Scala 运行在 Java 平台上,可以与所有的 Java 库无缝交互。

1. Function evaluations

Scala 是纯粹的面向对象式的编程,所有的 value 都是一个对象。
Scala 是函数式的编程,它把每一个函数看做一个 value,函数即对象。

1.1 函数的副作用

纯函数(Pure Function)是这样一种函数——输入输出数据流全是显式(Explicit)的。

显式(Explicit)的意思是,函数与外界交换数据只有一个唯一渠道——参数和返回值;函数从函数外部接受的所有输入信息都通过参数传递到该函数内部;函数输出到函数外部的所有信息都通过返回值传递到该函数外部。

隐式(Implicit)的意思是,函数通过参数和返回值以外的渠道,和外界进行数据交换。比如,读取全局变量,修改全局变量,都叫作以隐式的方式和外界进行数据交换;比如,利用I/O API(输入输出系统函数库)读取配置文件,或者输出到文件,打印到屏幕,都叫做隐式的方式和外界进行数据交换。

Pure Function的好处主要有几点:

  1. 无状态,Stateless。线程安全。不需要线程互斥或同步。
  2. Pure Function相互调用组装起来的函数,还是Pure Function。
  3. 应用程序或者运行环境(Runtime)可以对Pure Function的运算结果进行缓存,运算加快速度。

以上关于 pure function 的描述引用这篇博客。
另外一篇博客也介绍函数的副作用,推荐看看!

在函数式编程语言里面,函数是一等公民,这意味着:

  • 像其他 value,可以在任何地方定义函数,包括函数内部
  • 像其他 value,函数可以作为其他函数的输入参数或者返回值
  • 像其他 value,函数也有自己的操作符进行组合等操作

1.2 参数调用的区别

scala 函数定义格式如下:

  • call by value
    遇到表达式就计算,scala 函数参数默认进入函数内部之前计算出 value,再传进内部,关键字 val 也是立即计算模式
  • call by name
    惰性求值,这是函数式编程里面一个非常重要的概念。要用到的时候才计算,可以用def fun(x:Int, y: => Int) = ... 中的 =>显式调用。关键字 def 也是这种模式

举个例子:

def loop: Int = loop    // 定义一个死循环,def 可以,val 不行
def constOne(x: Int, y: => Int) = 1 // 第一个参数x: call by value, 第二个参数y: call by name
constOne(1+2, loop)    // loop一直没用,所以没有计算,输出1
constOne(loop, 1+2)    // 遇到loop立即计算,陷入死循环

1.3 牛顿法求平方根

  • Scala 中递归函数需要写返回值类型,非递归不一定要写。
  • 函数定义可以放在函数内部,防止命名污染。

牛顿法的基本思路是递归,首先猜测为 1.0,如果误差过大,则猜测值改为 guess 与 x / guess 的平均值。

object session {def abs(x: Double) = if (x>=0) x else (-x)   def sqrt(x: Double) = {def sqrtIter(guess: Double): Double =if (isGoodEnough(guess)) guesselse sqrtIter(improve(guess))def isGoodEnough(guess: Double) =abs(guess * guess - x) / x < 0.001def improve(guess: Double) =(guess + x / guess) / 2sqrtIter(1.0)}
sqrt(2.0)
}

1.4 尾递归

这篇博客推荐看看——递归与尾递归总结。

递归相对常用的算法如普通循环等,运行效率较低。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,因此递归次数过多容易造成栈溢出。

尾递归对应 imperative program(如c++)中的循环。 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量(需要保存的变量可以放在函数参数列表里)。它的栈是常数,可复用,与循环一样高效。

递归:

// 阶乘函数
def factorial(n: Int): Int =if (n == 0) 1 else n * factotial(n - 1)

尾递归:

// 阶乘函数
def factorial(n: Int) = {def loop(acc: Int, n: Int): Int=if (n == 0) accelse loop(acc * n, n - 1)loop(1, n)}



------------------------------部分二---------------------------------------

2. Higher Order Functions

把其他函数作为参数或者作为返回值,就是 higher order functions,python 里面也可以看到这样使用的情形。在酷壳上的博客有一个例子就是将函数作为返回值。

2.1 匿名函数

在 python 里边叫 lambda 函数,常常与 map(), filter(), reduce() 联合使用,前面也写过一篇这样的博客。举一个 scala 的 reduce 的例子,f: Int => Int 表示 f 是一个整数映射到整数的函数,计算下面公式:
∑bn=af(n)
def sum(f: Int => Int, a: Int, b: Int): Int = {def loop(a: Int, acc: Int): Int =if (a > b) accelse loop(a + 1, f(a) + acc)loop(a, 0)
}
def sumInts(a: Int, b: Int) = sum(x => x, a, b)  // f(n)=n
def sumCubes(a: Int, b: Int) = sum(x => x * x * x, a, b)  // f(n)=n*n*n
println(sumInts(2, 7))   //求和
println(sumCubes(3, 10)) //求立方和

2.2 currying

把一个函数的多个参数分解成多个函数, 然后把函数多层封装起来,每层函数都返回一个函数去接收下一个参数这样,可以简化函数的多个参数。
// sum 返回函数 sumF,风格与 python 相似def sum(f: Int => Int): (Int, Int) => Int = {def sumF(a: Int, b:Int): Int =if (a > b) 0 else f(a) + sumF(a + 1, b)sumF}           def sumInts = sum(x => x)      def sumCubes = sum(x => x * x * x) sumInts(1, 5)            //> res0: Int = 15sumCubes(1, 5)           //> res1: Int = 225sum(x=>x)(1, 5)          //> res2: Int = 15(sum(x=>x))(1, 5)        //> res3: Int = 15
更为简短的写法:
def sum(f: Int => Int)(a: Int, b: Int): Int =if (a > b) 0 else f(a) + sum(f)(a + 1, b)sum(x => x)(1, 5)  // 第一个()相当于创建了一个匿名函数
mapReduce 实现过程包括 map 一一映射函数和 reduce 函数及单位元素 zero(乘为1,加为0),参数包括序列区间 [a, b] 两个参数,假设我们求 [a, b] 区间上所有元素的平方和:
def mapReduce(map: Int => Int, reduce: (Int, Int) => Int, zero: Int)(a: Int, b: Int): Int = if (a > b) zeroelse reduce(map(a), mapReduce(map, reduce, zero)(a + 1, b))
def sumOfSquare(a: Int, b: Int) = mapReduce(x => x*x, (x, y) => x + y, 0)(a, b) //这里确定了三个,留下参数a,b
比如求立方和,四次方和等,更灵活的用法是 map 和 reduce 可以先指定一个reduce(都是sum),使用时再指定另一个(map),代码就不贴了。总之,所有mapreduce设置,包括map,reduce, zero, a, b都可以无序设置,替换组合成包含不同参数列表的新函数。

2.3 类

构造一个分数(rational)类,实现加减、比大小等基本功能。
object rationals {val x = new Rational(1, 3)              //> x  : week3.Rational = 1/3val y = new Rational(5, 7)              //> y  : week3.Rational = 5/7val z = new Rational(3)                 //> z  : week3.Rational = 3/1x.numer                                 //> res0: Int = 1x.sub(y).sub(z)                         //> res1: week3.Rational = 71/-21y.add(y)                                //> res2: week3.Rational = 10/7x.less(y)                               //> res3: Boolean = truex.max(y)                                //> res4: week3.Rational = 5/7
}class Rational(x: Int, y: Int) {require(y != 0, "denomitor must be nonzero")// scala 的构造函数就是执行bodydef this(x: Int) = this(x, 1) // 第二种构造函数, 补全到第一种private def gcd(a: Int, b: Int): Int =if (b==0) a else gcd(b, a % b) //private函数,求最大公约数val numer = x / gcd(x, y)  // 每次构造新类,都化简val denom = y / gcd(x, y)  // val,gcd函数只计算一次def add(that: Rational) =new Rational(numer * that.denom + denom * that.numer, // 交叉相乘相加denom * that.denom)def neg: Rational = new Rational(-numer, denom)def sub(that: Rational) = add(that.neg)def less(that: Rational) = numer * that.denom < that.numer * denomdef max(that: Rational) = if (this.less(that)) that else this // this 关键字,表示使用该method的objectoverride def toString = numer + "/" + denom // 每次打印类的格式
}

2.4 操作符

c++里面有操作符的重载,在scala里面技术层面上来说没有操作符这个概念。比如 1 + 2 实际是 1.+(2)。 + 是对象 1 的一种方法。Scala 实现 1 + 2 这种写法需要两种技术,以上面的例子来分析:
  1. 上面的例子中 r.add(s) 可以写成 r add s,任何只包含一个参数的方法都可以写成这样的形式,这种做法叫Infix Notation
  2. 但问题是,现在整数的加法是 a + b,分数的加法如果是 a add b,风格不一致。还有一个方法叫Relaxed Identifiers。大概意思是标志符不仅可以是字母开头的字符串组成,还可以是运算符(如果后面是冒号,加至少一个空格,否则会将冒号也看出标志的一部分)。
实现与整数加法风格一致的分数运算,代码如下:
package week3object rationals {val x = new Rational(1, 3)              //> x  : week3.Rational = 1/3val y = new Rational(5, 7)              //> y  : week3.Rational = 5/7val z = new Rational(3)                 //> z  : week3.Rational = 3/1-x                                      //> res0: week3.Rational = 1/-3x - y - z                               //> res1: week3.Rational = 71/-21y + y                                   //> res2: week3.Rational = 10/7x < y                                   //> res3: Boolean = truex * x + z * z                           //> res4: week3.Rational = 82/9
}class Rational(x: Int, y: Int) {require(y != 0, "denomitor must be nonzero")def this(x: Int) = this(x, 1)private def gcd(a: Int, b: Int): Int =if (b==0) a else gcd(b, a % b)val numer = x / gcd(x, y)val denom = y / gcd(x, y)def + (that: Rational) =new Rational(numer * that.denom + denom * that.numer,denom * that.denom)def unary_- : Rational = new Rational(-numer, denom) // unary_:一元运算符和二元运算符不同,一元要特地指出def - (that: Rational) = this + -thatdef < (that: Rational) = numer * that.denom < that.numer * denomdef * (that: Rational) = new Rational(numer * that.numer, denom * that.denom)override def toString = numer + "/" + denom // 打印类的格式
}
注意到上面代码中 x*x + z*z 没用括号也能计算出准确的结果,这是因为 scala 通用一套根据标识符确定运算优先级的规则表。


------------------------------部分三---------------------------------------

3. Data and Abstraction

3.1 Class Hierarchies

这一集字幕不同步-,-,听得有点费力!类的概念和其他语言里面很相似,基类,子类,父类啥的叫法差不多。在 Scala 中,所有用户自定义的类都是另外一个类的子类,如果没有显式给定父类,java 里面默认继承 java.lang,scala 里面是 Object。无论基类中的方法有没有具体实现,子类都可以用 override 重新定义,回想起之前强大的 toString 了吗?举一个二叉树的例子:
package week3object insets {val t1 = new NonEmpty(3, Empty, Empty)          //> t1  : week3.NonEmpty = {.3.}val t2 = t1 incl 4                              //> t2  : week3.IntSet = {.3{.4.}}val t3 = new NonEmpty(5, Empty, Empty)          //> t3  : week3.NonEmpty = {.5.}t2 union t3                                     //> res0: week3.IntSet = {{{.3.}4.}5.}
}abstract class IntSet {  // 抽象类作为基类,无法实例化,定义了三个接口def contains(x: Int): Boolean // 查找是否包含 xdef incl(x: Int): IntSet // 如果 x 不存在,将 x 放入二叉树中def union(x: IntSet): IntSet  // 两棵树融合}object Empty extends IntSet { // Empty 是 IntSet 的 subclass,`object` 表示单例模式,所有空节点都可以用一个对象来表示def contains(x: Int): Boolean = falsedef incl(x: Int): IntSet = new NonEmpty(x, Empty, Empty)def union(other: IntSet): IntSet = otheroverride def toString = "." // 空节点打印"."
}class NonEmpty(elem: Int, left: IntSet, right: IntSet) extends IntSet {def contains(x: Int): Boolean =if (x < elem) left contains xelse if (x > elem) right contains xelse truedef incl(x: Int): IntSet =// 实际上创建了一个新树,新树和旧树共用未改变的子树// 这个叫 persistent data structure,是把函数式编程扩展到 collections 的关键之一// 反正他是这么说的 `-,-`if (x < elem) new NonEmpty(elem, left incl x, right) // 一重一重地复制节点else if (x > elem) new NonEmpty(elem, left, right incl x)else thisdef union(other: IntSet): IntSet =((left union right) union other) incl elem  // override def toString = "{" + left + elem + right + "}" //强大的递归啊
}

3.2 How Classes Are Organized

没学过 java,估计和 java 中 package 管理一样。在源码最顶端写上 package week3 表示这个文件的 object 或者 class 属于这个包。要使用某一个类,可以在源码中用全名 week3.classA,也可以像 python 一样在最开始 import,源码中间用类名:
  • import week3.classA:导入类 classA
  • import week3.{classA, classB}:导入两个类
  • import week3._ :导入包所有(通配符导入方法)
除了从包导入,还可以从 object 导入。所有 Scala 程序默认导入一些 entities,比如 scala 中的 Int,java.lang 中的 Object,scala.Predef 中的断言等。更多信息可以查看 scala 的标准库。在 java 和 scala 中,一个类只能有一个父类(单继承),如何实现多继承,scala 中采用 traits。trait 像 java 里面的接口,偏抽象,但是更强大,可以包含 field 和具体方法,但是不能有value参数。子类可只能继承一个父类,但是可以继承任意多个 traits,例如:class Square extends Shape with Planar with Moveble。scala 类型结构如下,实线表示继承,虚线表示隐式转化。
  • Any是所有类型的基本类,包含的方法有:‘==’,‘!=’,‘equals’,‘hashCode’,‘toString’
  • AnyVal是数值类型的基本类。
  • AnyRef是所有引用类型的基本类,也是 java.lang.Object 的别名。
  • Nothing是所有类的子类型。主要作用是异常类与collection中的一个空元素。
  • Null 是所有类的子类型。但是与 AnyVal 的子类型不兼容。
Q:if (true) 1 else False 的返回类型是什么?A:int 和 boolean 类型,返回父类 AnyVal。

3.3 Polymorphism

Polymorphism 意味着函数可以以多种类型出现。一张 PPT 总结 Polymorphism:假设我们要建立一个 list 类,它可能包含了不同的数据类型(整数,布尔,list自身类型等),例子如下:这时需要用泛型来表示。新建一个package叫week4,在其中新建一个 trait。它的两个‘子类’分别为 Cons 和 Nil,分别表示含有元素的节点和空节点。
package week4// [T] 是类型参数,比如int,double之类。是泛型编程的基础
trait List[T] {def isEmpty: Booleandef head: Tdef tail: List[T]
}class Cons[T](val head: T, val tail: List[T]) extends List[T] {def isEmpty = false// head 和 tail 已经在初始化中实现
}class Nil[T] extends List[T] {def isEmpty = truedef head: Nothing = throw new NoSuchElementException("Nil.head")// Nothing 是任何类型的子类,所以也是 T 的子类def tail: Nothing = throw new NoSuchElementException("Nil.tail")
}
在 week4 中新建一个 scala worksheet,测试一下上述代码:
package week4import week4._object nth {// 创建一个含有一个元素的 listdef singleton[T](elem: T) = new Cons(elem, new Nil)//> singleton: [T](elem: T)week4.Cons[T]singleton[Int](3)         //> res0: week4.Cons[Int] = week4.Cons@71be98f5singleton(3) // 编译器可以从 3 推到出 T 是 Int // 寻找 list 中的第 n 个元素def nth[T](n: Int, xs: List[T]): T =if (xs.isEmpty) throw new IndexOutOfBoundsExceptionelse if (n == 0) xs.headelse nth(n - 1, xs.tail)        //> nth: [T](n: Int, xs: week4.List[T])T// 创建一个 list  = [1, 2, 3]val list = new Cons(1, new Cons(2, new Cons(3, new Nil)))nth(2, list)         //> res2: Int = 3
}

小记

这里是课程前四次的大概内容,因为第一次课是教你怎么安装,所以实际内容只有三次课,后面还有四次课。总体来说,函数式编程给人很多启发,但是如果不是真正需要用,也不宜占用太多时间去学习。暑假要去实习了,等下学期再学吧。


from:http://www.cnblogs.com/daniel-D/

Scala 中的函数式编程基础相关推荐

  1. Scala 中的函数式编程基础(一)

    主要来自 Scala 语言发明人 Martin Odersky 教授的 Coursera 课程 <Functional Programming Principles in Scala>. ...

  2. Scala中的函数式编程与面向对象编程知识点复习整理(二)——面向对象编程

    面向对象基础 概述 Scala是一门完全面向对象的语言,摒弃了Java中很多不是面向对象的语法,虽然如此,但其面向对象思想和Java的面向对象思想还是一致的. package 在java中     作 ...

  3. 函数式编程基础_在收件箱中免费学习函数式编程的基础

    函数式编程基础 by Preethi Kasireddy 通过Preethi Kasireddy 在收件箱中免费学习函数式编程的基础 (Learn the fundamentals of functi ...

  4. Java 中的函数式编程

    1. 概述 在本教程中,我们将了解函数式编程范式的核心原则以及如何在 Java 编程语言中使用它们. 我们还将介绍一些高级函数式编程技术.这将帮助我们了解 Java 中的函数式编程的好处. 2. 什么 ...

  5. javascript函数式_JavaScript中的函数式编程—结合实际示例(第1部分)

    javascript函数式 by rajaraodv 通过rajaraodv JavaScript中的函数式编程-结合实际示例(第1部分) (Functional Programming In Jav ...

  6. javascript函数式_JavaScript中的函数式编程—结合实际示例(第2部分)

    javascript函数式 by rajaraodv 通过rajaraodv JavaScript中的函数式编程-结合实际示例(第2部分) (Functional Programming In Jav ...

  7. wpf绑定 dictionary 给定关键字不再字典中_为什么要在 JavaScript 中学习函数式编程?...

    请忘掉你认为你知道的有关 JavaScript 的任何东西,以初学者心态来接触这份资料. 为帮助你这样做,我们打算从头开始复习 JavaScript 的基础知识, 就好像你以前从来没有看到过 Java ...

  8. 在 Python 中使用函数式编程的最佳实践!

    在函数式编程中,如何使用 Python 编写出优秀的代码? 作者 | Amandine Lee 译者 | 弯月 责编 | 屠敏 出品 | CSDN(ID:CSDNNews) 简介 Python 是一种 ...

  9. python支持函数式编程吗_利用Fn.py库在Python中进行函数式编程

    尽管Python事实上并不是一门纯函数式编程语言,但它本身是一门多范型语言,并给了你足够的自由利用函数式编程的便利.函数式风格有着各种理论与实际上的好处(你可以在Python的文档中找到这个列表): ...

最新文章

  1. 互联网公益平台米多乐获近千万天使轮融资,熊猫资本投资...
  2. 大专python工资有多高-作为Python程序员,薪资一般是多少?
  3. 如何在一台电脑上同时启动多个tomcat,及如何解决tomcat一闪而过
  4. VBScript教程-第一章. 脚本的定义和术语
  5. STM32开发 -- 自动连接一个未保存过的WIFI
  6. 【java】Java异常处理总结
  7. socket 通信关于bind那点事
  8. C语言家谱管理程序,课内资源 - 基于C语言实现的家谱管理系统
  9. 竖流式沉淀池集水槽设计计算_竖流式沉淀池的设计
  10. python requests form data_Python爬虫:Request Payload和Form Data的简单区别说明
  11. struts2:JSON在struts中的应用(JSP页面中将对象转换为JSON字符串提交、JSP页面中获取后台Response返回的JSON对象)...
  12. 计算机会考补考时间安排,2019-2020学年第二学期初补考考试安排(实时更新)
  13. 牛客练习赛20:A. 礼物(组合数学/小球与盒子问题)
  14. Java中的list---ArrayList与LinkedList
  15. matlab无缝拼接两个图_无色差液晶拼接屏研发商参数
  16. 【第六课】Smart 3d常见问题集锦
  17. android studio scala插件,在Android Studio中使用Scala和Java
  18. google talk
  19. The APR based Apache Tomcat Native library which allows optimal performance in production environm
  20. vue {{}}中小数保留几位小数的toFixed(小数位数)方法

热门文章

  1. 多核学习在图像分类中的应用
  2. mysql 有always on么,mysql 关于时间类型的刨坑之路
  3. Spring Cloud Alibaba源码 - 22 Feign 源码解析
  4. Spring JDBC-混合框架的事务管理
  5. 学习笔记Hadoop(十四)—— MapReduce开发入门(2)—— MapReduce API介绍、MapReduce实例
  6. php留言板项目经验,php工程师简历项目经验填写样本
  7. python 链表中倒数第k个节点
  8. 创建一个类 new 与 不加new 有什么区别?
  9. 信号通路怎么读_奥利给~~代谢通路富集分析原来还可以这么做?
  10. 外键查询_传统关系型数据库查询性能提高思路