参考这篇文章:javascript
http://www.ibm.com/developerworks/cn/java/j-lo-funinscala1/html
这也是一个系列java
严格意义上的编程范式分为:命令式编程(Imperative Programming)、函数式编程(Functional Programming)和逻辑式编程(Logic Programming)node
清单 1. 数列求和git
//xs.head 返回列表里的头元素,即第一个元素 //xs.tail 返回除头元素外的剩余元素组成的列表 def sum(xs: List[Int]): Int = if (xs.isEmpty) 0 else xs.head + sum(xs.tail)
def max(xs: List[Int]): Int = { if (xs.isEmpty) throw new java.util.NoSuchElementException if (xs.size == 1) xs.head else if (xs.head > max(xs.tail)) xs.head else max(xs.tail) }
def reverse(xs: String): String = if (xs.length == 1) xs else reverse(xs.tail) + xs.head
def quickSort(xs: List[Int]): List[Int] = { if (xs.isEmpty) xs else quickSort(xs.filter(x=>x<xs.head)):::xs.head::quickSort(xs.filter(x=>x>xs.head)) }
习惯于命令式编程范式的程序员还有一个担心:相比循环,递归不是存在效率问题吗?每一次递归调用,都会分配一个新的函数栈,若是递归嵌套很深,容易出现栈溢出的问题。好比下面计算阶乘的递归程序:程序员
def factorial(n: Int): Int = if (n == 0) 1 else n * factorial(n - 1)
当 n
很大时,函数栈将很快被耗尽。然而尾递归能帮咱们解决这个问题,所谓尾递归是指在函数调用的最后一步,只调用该递归函数自己,此时,因为无需记住其余变量,当前的函数栈能够被重复使用。上面的程序只需稍微改造一下,既能够变成尾递归式的程序,在效率上,和循环是等价的。github
注:C++编译器对尾递归也进行了优化,可是编译器选项默认不打开,须要主动打开;Java没有对尾递归作优化。面试
def factorial(n: Int): Int = { @tailrec def loop(acc: Int, n: Int): Int = if (n == 0) acc else loop(n * acc, n - 1) loop(1, n) }
在上面的程序中,咱们在阶乘函数内部定义了一个新的递归函数,该函数最后一步要么返回结果,要么调用该递归函数自己,因此这是一个尾递归函数。该函数多出一个变量 acc
,每次递归调用都会更新该变量,直到递归边界条件知足时返回该值,即为最后的计算结果。这是一种通用的将非尾递归函数转化为尾递归函数的方法,你们可多加练习,掌握这一方法。对于尾递归,Scala 语言特别增长了一个注释 @tailrec
,该注释能够确保程序员写出的程序是正确的尾递归程序,若是因为疏忽大意,写出的不是一个尾递归程序,则编译器会报告一个编译错误,提醒程序员修改本身的代码。算法
把上面的解法写进程序里就是:编程
package com.spark.my import scala.annotation.tailrec object Hello { def main(args: Array[String]): Unit = { println("hello world") val ret = factorial(3) println("ret: %d".format(ret)) } def factorial(n: Int): Int = { @tailrec def loop(acc: Int, n: Int): Int = if (n == 0) acc else loop(n * acc, n - 1) loop(1, n) } }
运行结果:
hello world ret: 6 Process finished with exit code 0
也许有的读者看了上面的例子后,仍是感到不能信服:虽然使用递归会让程序变得简洁易懂,但我用循环也同样能够实现,大不了多几行代码而已,并且我还不用知道什么尾递归,写出的程序就是效率最高的。那咱们一块儿来看看下面这个问题:有趣的零钱兑换问题。题目大体以下:
假设某国的货币有若干面值,现给一张大面值的货币要兑换成零钱,问有多少种兑换方式。
解答:第一种找零方式总共有 countChange(money, coins.tail)
种,第二种找零方式等价为对于 money – conins.head
进行一样的兑换,则这种兑换方式有 countChange(money - coins.head, coins)
种,二者之和即为全部的零钱兑换方式。
写成代码:
package com.spark.my import scala.annotation.tailrec object Hello { def main(args: Array[String]): Unit = { println("hello world") val coins = List(1, 2, 5) val money = 10 val ret = count(money, coins) println("ret: %d".format(ret)) } def count(money: Int, coins: List[Int]): Int = { if (money == 0) 1 else if (coins.size == 0 || money < 0) 0 else count(money, coins.tail) + count(money - coins.head, coins) } }
运行:
hello world ret: 10 Process finished with exit code 0
可是注意,开始的时候,我在count前面加上了 @tailrec,可是报错,说这个不是尾递归。还不清楚,怎么把这个改为尾递归。
事实上,在 Haskell 语言中,不存在 while、for 等命令式编程语言中必不可少的循环控制语句,Haskell 强迫程序员使用递归等函数式编程的思惟去解决问题。做者也鼓励你们之后碰到问题时,先考虑有没有好的递归的方式实现,看看是否会为咱们关于编程的理解带来新的思考。
而Scala融合了Functional programming 和 OO programming,有class和Object.
开始看第二篇了:
http://www.ibm.com/developerworks/cn/java/j-lo-funinscala2/
Scala 语法很是简洁,拥有其余语言编程经验的程序员很容易读懂 Scala 代码。如今咱们将回过头来,从基本的语法开始学习 Scala 语言。你们会发现 Scala 语言异常精炼,实现一样功能的程序,在代码量上,使用 Scala 实现一般比 Java 实现少一半或者更多。短小精悍的代码经常意味着更易懂,更易维护。本文将为你们介绍 Scala 语言的基本语法,帮助你们写出本身的第一个 Scala 程序。
Scala 为定义变量提供了两种语法。
使用 val
定义常量,一经定义后,该变量名不能被从新赋值。
使用 var
定义变量,可被从新赋值。
在 Scala 中,鼓励使用 val
,除非你有明确的需求使用 var
。对于 Java 程序员来讲,刚开始可能会以为有违直觉,但习惯后你会发现,大多数场合下咱们都不须要 var
,一个可变的变量。
val x = 0 var y = 1 y = 2 // 给常量赋值会出现编译错误 // x = 3 // 显式指定变量类型 val x1: Int = 0 var y1: Int = 0
定义变量时没有指定变量类型。这是否意味着 Scala 是和 Python 或者 Ruby 同样的动态类型语言呢?
偏偏相反,Scala 是严格意义上的静态类型语言,因为其采用了先进的类型推断(Type Inference)技术,程序员不须要在写程序时显式指定类型,编译器会根据上下文推断出类型信息。好比变量 x
被赋值为 0,0 是一个整型,因此 x
的类型被推断出为整型。固然,Scala 语言也容许显示指定类型,如变量 x1
,y1
的定义。通常状况下,咱们应尽可能使用 Scala 提供的类型推断系统使代码看上去更加简洁。
函数的定义也很是简单,使用关键字 def
,后跟函数名和参数列表,若是不是递归函数能够选择省略函数返回类型。
Scala 还支持定义匿名函数,匿名函数由参数列表,箭头链接符和函数体组成。
// 定义函数 def square(x: Int): Int = x * x // 若是不是递归函数,函数返回类型可省略 def sum_of_square(x: Int, y: Int) = square(x) + square(y) sum_of_square(2, 3) // 定义匿名函数 val cube = (x: Int) => x * x *x cube(3) // 使用匿名函数,返回列表中的正数 List(-2, -1, 0, 1, 2, 3).filter(x => x > 0)
Scala 中的一条语句实际上是一个表达式,函数的执行过程就是对函数体内的表达式的求值过程,最后一条表达式的值就是函数的返回值。若是函数体只包含一条表达式,则能够省略 {}
。其次,没有显式的 return
语句,最后一条表达式的值会自动返回给函数的调用者。
和 Java 不一样,在 Scala 中,函数内部还能够定义其余函数。好比上面的程序中,若是用户只对 sum_of_square 函数感兴趣,则咱们能够将 square 函数定义为内部函数,实现细节的隐藏。
def sum_of_square(x: Int, y: Int): Int = { def square(x: Int) = x * x square(x) + square(y) }
和 Java 中对应的条件判断语句不一样,Scala 中的 if else
是一个表达式,根据条件的不一样返回相应分支上的值。好比下面例子中求绝对值的程序,因为 Scala 中的 if else
是一个表达式,因此不用像 Java 那样显式使用 return
返回相应的值。
def abs(n: Int): Int = if (n > 0) n else -n
和 Java 同样,Scala 提供了用于循环的 while 语句,在下面的例子中,咱们将借助 while 循环为整数列表求和。
def sum(xs: List[Int]) = { var total = 0 var index = 0 while (index < xs.size) { total += xs(index) index += 1 } total }
上述程序是习惯了 Java 或 C++ 的程序员想到的第一方案,但仔细观察会发现有几个问题:
首先,使用了 var
定义变量,咱们在前面说过,尽可能避免使用 var
。
其次,这个程序太长了,第一次拿到这个程序的人须要对着程序仔细端详一会:程序首先定义了两个变量,并将其初始化为 0
,而后在 index
小于列表长度时执行循环,在循环体中,累加列表中的元素,并将 index
加 1
,最后返回最终的累加值。
直到这时,这我的才意识到这个程序是对一个数列求和。
让咱们换个角度,尝试用递归的方式去思考这个问题,对一个数列的求和问题能够简化为该数列的第一个元素加上由后续元素组成的数列的和,依此类推,直到后续元素组成的数列为空返回 0。具体程序以下,使用递归,原来须要 9 行实现的程序如今只须要两行,并且程序逻辑看起来更清晰,更易懂。(关于如何使用递归的方式去思考问题,请参考做者的另一篇文章《使用递归的方式去思考》)(已在本文的前半部分学习并引用)
//xs.head 返回列表里的头元素,即第一个元素 //xs.tail 返回除头元素外的剩余元素组成的列表 def sum1(xs: List[Int]): Int = if (xs.isEmpty) 0 else xs.head + sum1(xs.tail)
有没有更简便的方式呢?答案是确定的,咱们可使用列表内置的一些方法达到一样的效果:
xs.foldLeft(0)((x0, x) => x0 + x)
该方法传入一个初始值 0,一个匿名函数,该匿名函数累加列表中的每个元素,最终返回整个列表的和。使用上面的方法,咱们甚至不须要定义额外的方法,就能够完成一样的操做。
事实上,List 已经为咱们提供了 sum 方法,在实际应用中,咱们应该使用该方法,而不是本身定义一个。做者只是但愿经过上述例子,让你们意识到 Scala 虽然提供了用于循环的 while 语句,但大多数状况下,咱们有其余更简便的方式可以达到一样的效果。
可假设其平方根为任意一个正数 ( 在这里,咱们选定 1 为初始的假设 ),而后比较 x
与该数的平方,若是二者足够近似(好比二者的差值小于 0.0001),则该正数即为 x
的平方根;不然从新调整假设,假设新的平方根为 上次假设
与 x/ 上次假设
的和的平均数。经过下表能够看到,通过仅仅 4 次迭代,就能求解出至关精确的 2 的平方根。
将上述算法转化为 Scala 程序,首先咱们定义这个迭代过程,这也是该算法的核心部分,所幸这一算法很是简单,利用递归,一个 if else
表达式就能搞定。后续为两个辅助方法,让咱们的程序看起来更加清晰。最后咱们选定初始假设为 1
,定义出最终的 sqrt
方法。
代码以下:
package com.spark.my import scala.annotation.tailrec object Hello { def main(args: Array[String]): Unit = { // 测试代码 val ret = sqrt(2) println(ret) } // 迭代函数,若解不知足精度,经过递归调用接续迭代 def sqrtIter(guess: Double, x: Double): Double = if (isGoodEnough(guess, x)) guess else sqrtIter((guess + x / guess)/2, x) // 判断解是否知足要求 def isGoodEnough(guess: Double, x: Double) = abs(guess * guess - x)< 0.0001 // 辅助函数,求绝对值 def abs(x: Double) = if (x < 0) -x else x // 目标函数 def sqrt(x: Double): Double = sqrtIter(1, x) }
然而这段程序也有一个显而易见的缺陷,做为用户,他们只关心 sqrt
函数,但这段程序却将其余一些辅助函数也暴露给了用户,咱们在前面提到过,Scala 里能够嵌套定义函数,咱们能够将这些辅助函数定义为 sqrt
的内部函数,更进一步,因为内部函数能够访问其函数体外部定义的变量,咱们能够去掉这些辅助函数中的 x
参数。最终的程序以下:
package com.spark.my import scala.annotation.tailrec object Hello { def main(args: Array[String]): Unit = { // 测试代码 val ret = sqrt(3) println(ret) } // 目标函数,经过将须要用到的辅助函数定义为内部函数,实现细节的隐藏 def sqrt(x: Double): Double = { // 迭代函数,若解不知足精度,经过递归调用接续迭代 def sqrtIter(guess: Double): Double = if (isGoodEnough(guess)) guess else sqrtIter((guess + x / guess) / 2) // 判断解是否知足要求 def isGoodEnough(guess: Double) = abs(guess * guess - x) < 0.0001 // 辅助函数,求绝对值 def abs(x: Double) = if (x < 0) -x else x sqrtIter(1) } }
做为应用程序执行时,咱们须要在一个单例对象中定义入口函数 main
,通过编译后就能够执行该应用程序了。
object HelloWorld { def main(args: Array[String]): Unit = { println("Hello World!") } }
Scala 还提供了一个更简便的方式,直接继承另外一个对象 App,无需定义 main
方法,编译便可运行。
object HelloScala extends App { println("Hello Scala!") }
亲测,可用。
本文为你们介绍了 Scala 的基本语法,相比 Java,Scala 的语法更加简洁,好比 Scala 的类型推断能够省略程序中绝大多数的类型声明,短小精悍的匿名函数能够方便的在函数之间传递,还有各类在 Scala 社区约定俗成的习惯,好比省略的分号以及函数体只有一条表达式时的花括号,这一切都帮助程序员写出更简洁,更优雅的程序。限于篇幅,本文只介绍了 Scala 最基本的语法,若是读者想跟进一步学习 Scala,请参考 Scala 的 官方文档及文末所附的参考资源。
掌握了这些基本的语法,咱们将在下一篇文章中为你们介绍如何使用 Scala 进行函数式编程,这是 Scala 最让人心动的特性之一,对于习惯了面向对象的程序员来讲,学习 Scala 更多的是在学习如何使用 Scala 进行函数式编程。
开始学习这篇文章
http://www.ibm.com/developerworks/cn/java/j-lo-funinscala3/index.html
即便你是一个刚刚踏入职场的新人,若是在面试时能有意无心地透露出你懂那么一点点函数式编程,也会让你的面试官眼前一亮。
阿兰·图灵(Alan Turing)和约翰·冯·诺伊曼(John von Neumann)。阿兰·图灵提出了图灵机的概念,约翰·冯·诺伊曼基于这一理论,设计出了第一台现代计算机。因为图灵以及冯·诺伊曼式计算机的大获成功,历史差点淹没了另一位一样杰出的科学家和他的理论,那就是阿隆佐·邱奇(Alonzo Church)和他的λ演算。
阿隆佐·邱奇是阿兰·图灵的老师,上世纪三十年代,他们一块儿在普林斯顿研究可计算性问题,为了回答这一问题,阿隆佐·邱奇提出了λ演算,其后不久,阿兰·图灵提出了图灵机的概念,尽管形式不一样,但后来证实,两个理论在功能上是等价的,条条大路通罗马。
若是不是约翰·麦卡锡(John McCarthy),阿隆佐·邱奇的λ演算恐怕还要在历史的故纸堆中再多躺几十年,约翰·麦卡锡是人工智能科学的奠定人之一,他发现了λ演算的珍贵价值,发明了基于λ演算的函数式编程语言:Lisp,因为其强大的表达能力,一推出就受到学术界的热烈欢迎,以致于一段时间内,Lisp 成了人工智能领域的标准编程语言。
很快,λ演算在学术界流行开来,出现了不少函数式编程语言:Scheme 、SML、Ocaml 等,可是在工业界,仍是命令式编程语言的天下,Fortran、C、C++、Java 等。
随着时间的流逝,愈来愈多的计算机从业人员认识到函数式编程的意义,爱立信公司于上世纪八十年代开发出了 Erlang 语言来解决并发编程的问题;在互联网的发展浪潮中,愈来愈多的语言也开始支持函数式编程:JavaScript、Python、Ruby、Haskell、Scala 等。能够预见,若是过去找一个懂什么是函数式编程的程序员很困难,那么在不久的未来,找一个一点也没听过函数式编程的程序员将更加困难。
狭义地说,函数式编程没有可变的变量、循环等这些命令式编程方式中的元素,像数学里的函数同样,对于给定的输入,无论你调用该函数多少次,永远返回一样的结果。而在咱们经常使用的命令式编程方式中,变量用来描述事物的状态,整个程序,不过是根据不断变化的条件来维护这些变量。
广义地说,函数式编程重点在函数,函数是这个世界里的一等公民,函数和其余值同样,能够处处被定义,能够做为参数传入另外一个函数,也能够做为函数的返回值,返回给调用者。利用这些特性,能够灵活组合已有函数造成新的函数,能够在更高层次上对问题进行抽象。本文的重点将放在这一部分。
约翰·巴克斯(John Backus)为人熟知的两项成就是 FORTRAN 语言和用于描述形式系统的巴克斯范式,由于这两项成就,他得到了 1977 年的图灵奖。
有趣的是他在获奖后,作了一个关于函数式编程的讲演:Can Programming Be Liberated From the von Neumann Style? 1977 Turing Award Lecture。他认为像 FORTRAN 这样的命令式语言不够高级,应该有新的,更高级的语言能够摆脱冯诺依曼模型的限制,因而他又发明了 FP 语言,虽然这个语言未获成功,可是约翰·巴克斯关于函数式编程的论述却获得了愈来愈多的承认。
下面,咱们就罗列一些函数式编程的优势。
首先,函数式编程自然有并发的优点。因为工艺限制,摩尔定律已经失效,芯片厂商只能采起多核策略。程序要利用多核运算,必须采起并发,而并发最头疼的问题就是共享数据,狭义的函数式编程没有可变的变量,数据只读不写,并发的问题迎刃而解。这也是前面两篇文章中,一直建议你们在定义变量时,使用 val 而不是 var 的缘由。爱立信公司发明的 Erlang 语言就是为解决并发的问题而生,在电信行业取得了不俗的成绩。
其次,函数式编程有迹可寻。因为不依赖外部变量,给定输入函数的返回结果永远不变,对于复杂的程序,咱们能够用值替换的方式(substitution model)化繁为简,轻松得出一段程序的计算结果。为这样的程序写单元测试也很方便,由于不用担忧环境的影响。
最后,函数式编程高屋建瓴。写程序最重要的就是抽象,不一样风格的编程语言为咱们提供了不一样的抽象层次,抽象层次越高,表达问题越简洁,越优雅。读者从下面的例子中能够看到,使用函数式编程,有一种高屋建瓴的感受。
def cube(n: Int) = n * n * n cube(35) cube(68)
def cube(n: Int) = n * n * n def sumCube(a: Int, b: Int): Int = if (a > b) 0 else cube(a) + sumCube(a + 1, b) sumCube(1, 10)
是时候教给他第二项本领了:高阶函数(Higher-Order Function),所谓高阶函数,就是操做其余函数的函数。以求和为例,咱们能够定义一个新的求和函数,该函数接受另一个函数做为参数,这个做为参数的函数表明了某种对数据的操做。使用高阶函数后,抽象层次提升,代码变得更简单了。
def cube(n: Int) = n * n * n def id(n: Int) = n def square(n : Int) = n * n def fact(n: Int): Int = if (n == 0) 1 else n * fact(n - 1) // 高阶函数 def sum(f: Int => Int, a: Int, b: Int): Int = if (a > b) 0 else f(a) + sum(f, a + 1, b) // 使用高阶函数从新定义求和函数 def sumCube(a: Int, b: Int): Int = sum(cube, a, b) def sumSquare(a: Int, b: Int): Int = sum(square, a, b) def sumFact(a: Int, b: Int): Int = sum(fact, a, b) def sumInt(a: Int, b: Int): Int = sum(id, a, b) // do it sumCube(1, 10) sumInt(1, 10) sumSquare(1, 10) sumFact(1, 10)
对于简单的函数,咱们还能够将其转化为匿名函数,让程序变得更简洁一些。在高阶函数中使用匿名函数,这是函数式编程中常常用到的一个技巧,多数状况下,咱们关心的是高阶函数,而不是做为参数传入的函数,因此为其单独定义一个函数是没有必要的。
值得称赞的是 Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体,参数的类型是可省略的,Scala 的类型推测系统会推测出参数的类型。使用匿名函数后,咱们的代码变得更简洁了:
def fact(n: Int): Int = if (n == 0) 1 else n * fact(n - 1) // 高阶函数 def sum(f: Int => Int, a: Int, b: Int): Int = if (a > b) 0 else f(a) + sum(f, a + 1, b) // 使用高阶函数从新定义求和函数 def sumCube(a: Int, b: Int): Int = sum(x => x * x * x, a, b) def sumSquare(a: Int, b: Int): Int = sum(x => x * x, a, b) def sumFact(a: Int, b: Int): Int = sum(fact, a, b) def sumInt(a: Int, b: Int): Int = sum(x => x, a, b) // 有了这些函数,小龙作起做业轻松多了 sumCube(1, 10) sumInt(1, 10) sumSquare(1, 10) sumFact(1, 10)
上面使用匿名函数后的高阶函数还有什么地方值得改进呢?但愿你们还会想起那句话:Don ’ t Repeat Yourself !求和函数的两个上下限参数 a
,
b
被重复得传来传去。咱们试着从新定义 sum
函数,让它接受一个函数做为参数,同时返回另一个函数。看到没?使用新的 sum
函数,咱们再定义各类求和函数时,彻底不须要这两个上下限参数了,咱们的程序又一次获得了简化。
def fact(n: Int): Int = if (n == 0) 1 else n * fact(n - 1) // 高阶函数 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 sumCube: Int = sum(x => x * x * x) def sumSquare: Int = sum(x => x * x) def sumFact: Int = sum(fact) def sumInt: Int = sum(x => x) // 这些函数使用起来还和原来同样 ! sumCube(1, 10) sumInt(1, 10) sumSquare(1, 10) sumFact(1, 10)
能不能再简单一点呢?既然 sum
返回的是一个函数,咱们应该能够直接使用这个函数,彷佛没有必要再定义各类求和函数了。
def fact(n: Int): Int = if (n == 0) 1 else n * fact(n - 1) // 高阶函数 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 sumCube: Int = sum(x => x * x * x) //def sumSquare: Int = sum(x => x * x) //def sumFact: Int = sum(fact) //def sumInt: Int = sum(x => x) // 直接调用高阶函数 ! sum(x => x * x * x) (1, 10) //=> sumCube(1, 10) sum(x => x) (1, 10) //=> sumInt(1, 10) sum(x => x * x) (1, 10) //=> sumSquare(1, 10) sum(fact) (1, 10) //=> sumFact(1, 10)
我本身在Intellij里面也写了一下这一块的功能(我用的是first-spark-demo这个project):
package com.spark.my import scala.annotation.tailrec object Hello{ def main(args: Array[String]): Unit = { val ret = sum(x=> x*x)(1, 2) println(ret) } 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 } }
这种返回函数的高阶函数极为有用,所以 Scala 为其提供了语法糖,上面的 sum
函数能够简写为:
我在Intellij里面实际写了一下:
package com.spark.my import scala.annotation.tailrec object Hello{ def main(args: Array[String]): Unit = { val ret = sum(x=> x*x)(1, 2) println(ret) } def sum(f: Int => Int)(a: Int, b: Int): Int = if (a > b) 0 else f(a) + sum(f)(a+1, b) }
注意,以上语法糖和原来的写法,仍是有不少区别的:
首先sum()()两个括号直接没有冒号
另外 ()(): Int 中间是冒号,不是=>
而后再下面的表达式中,用到了上面第二个括号里面定义的变量a和b
咱们把原来的 sum
函数转化成这样的形式,好处在哪里?答案是咱们得到了更多的可能性,好比刚开始求和的上下限还没肯定,咱们能够在程序中把一个函数传给 sum
,sum(fact)
彻底是一个合法的表达式,待后续上下限肯定下来时,再把另外两个参数传进来。
对于 sum 函数,咱们还能够更进一步,把 a,b 参数再转化一下,这样 sum 函数就变成了这样一个函数:它每次只能接收一个参数,而后返回另外一个接收一个参数的函数,调用后,又返回一个只接收一个参数的函数。
这就是传说中的柯里化,多么完美的形式!在现实世界中,的确有这样一门函数式编程语言,那就是 Haskell,在 Haskell 中,全部的函数都是柯里化的,即全部的函数只接收一个参数!
注:柯里化(currying):又称部分求值(Partial Evaluation),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,而且返回接受余下的参数并且返回结果的新函数的技术。
我在Intellij里面写的:
package com.spark.my import scala.annotation.tailrec object Hello{ def main(args: Array[String]): Unit = { val ret = sum(x=> x*x)(1)(2) println(ret) } def sum(f: Int => Int)(a: Int)(b: Int): Int = if (a > b) 0 else f(a) + sum(f)(a+1)(b)
}
能够看到,很是巧妙!至关于接收第一个参数,返回接受后续参数的函数,直到参数个数足够为止,得出最后的结果。
在 Scala 类库中,使用函数式编程的例子比比皆是,特别是对于列表的操做,将高阶函数的优点展现得淋漓尽致,限于篇幅,不能在本文中为你们做以介绍,做者将在后面的系列文章中,以 Scala 中的列表为例,详细介绍高阶函数在实战中的应用。(还没找到后面的文章在哪里)
后续再看看JS里面关于函数编程的部分吧。关键是把函数编程的思想领悟了。