首先咱们从scala的函数开始:javascript
在命令后输入:(x:Int) => x * 2java
这种奇怪的格式让咱们陌生,可是若是你熟悉javascript的函数以下:编程
function myfunc(param){数组
alert("hello" + param);函数
}spa
这是一个弹出窗口hello的函数,显示的是hellp+输入参数,这个param不仅是能够传入值,也能够传入另一个函数,为了可以传入另一个函数做为参数,被传入的函数在写法上要改变一下,好比:scala
var myfunc2 = function (param){code
alert("hello" + param);orm
}htm
能够看到,咱们将函数名称移到了左边,右边就剩余function和()以及{}三个符号,这样咱们能够传入myfunc了:
myfunc(myfunc2);
咱们已经理解了JS中函数传递,那么Scala中也是相似,上面的(x:Int) => x * 2 其实能够当作JS的(x:Int) { x * 2 } ,咱们使用大括号替代右箭头=>,二者意思差很少(少一个function付)。等同于js:
var myfunc3 = function (x) {
return x * 2 ;
}
scala的x:Int相似Java的Int x,Int是x的类型,js是动态语言,因此类型定义是不须要的。Scala的写法是:
var myfunc = (x:Int) => x * 2
咱们本身简写成(x:Int) => x * 2也能够,和myfunc同样处处引用使用,没有名称而已,也就是匿名函数,能够做为另一个函数的输入参数使用。如:
myfunc2( (x:Int) => x * 2);
myfunc这个函数本身也能够被直接使用:
myfunc(2)
结果是4;
那么myfunc(myfunc(2))是多少呢,注意,这里不是myfunc2,而是myfunc本身。
myfunc(2)
结果是8; 至关于调用了两次本身。
面向函数编程常常形象的比喻成相似集成电路的输入输出同样:
输入--->函数运算 -->输出
因此,这里(x:Int) => x * 2也有这三种结构:
输入x---->函数运算x * 2 ---->输出x*2的结果。
输出x*2结果和x*2运算实际是捆绑在一块儿,是一体的,因此,通常咱们就不显式象js中声明return x*2。Scala的"=>"符号的 右边能够认为表明细节,表明函数体,表明ReturnValue is “右边”.
有了前面热身,咱们对函数是第一等公民有个初步印象,下面再看看函数如何做为值传递的:
val myList = List(1,2,3,4,5)
for(x:Int <- myList) yield myfunc (x)
yield是专门用于for循环,将新的结果写入到结果序列中,这里将myfunc(x)结果返回一个新的List,结果是:List[Int] = List(2, 4, 6, 8, 10)
下面咱们引入面向函数编程最经常使用的一个函数map:
myList.map((x: Int) => x * 2)
结果也是List[Int] = List(2, 4, 6, 8, 10); 也至关以将集合myList中每一个元素通过(x: Int) => x * 2运算获得结果。
从输入输出这个角度理解这个函数map,map的输入是(x: Int) => x * 2的输出,而(x: Int) => x * 2的输入是什么呢?是x,那么x从哪里来的?猜想多是来自myList的每一个元素。
这里引入一个函数组合子(Functional Combinators)定义,组合子是一个没有自由free变量的函数。什么叫自由变量?没有孤立的变量,好比上面的变量是x不该该是一个孤立变量,其来自于myList的元素。
这里的map对列表myList中的每个元素都应用了 x * 2函数,并返回一个新的列表List(2, 4, 6)。咱们称这个操做是map 组合子,同理还有filter操做。
for(x <- c; if cond) yield {...}
等同于:
c.filter(x => cond).map(x => {...})
或
c.withFilter(x => cond).map(x => {...})
注意到这里for中多了一个if语句判断,也就是对列表集合中元素进行if判断,这至关于使用了filter函数,filter对传入函数计算结果为false的所有删除。返回一个布尔值的函数一般被称为谓词函数[或断定函数]。
再看看for的另一种形式, 集合嵌套:
for(x <- c1; y <- c2; z <-c3) {...}
等同于:
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
使用组合子foreach的好处这里也能够看出,使用for进行嵌套循环,常常可能会迷糊钻晕了,而使用组合子则简单明了。foreach 这个组合子相似Map,可是不返回任何结果,
val doubled = myList.foreach((x: Int) => x * 2) doubled: Unit = ()
这里foreach返回结果为类型Unit,相似void,是空。
flatMap 是另一个组合子,flat能够理解为折叠或嵌套的意思。
for(x <- c1; y <- c2; z <- c3) yield {...}
等同于
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
这里的for循环和上面区别多了一个yield,看到yield第一反应咱们是想到map,可是这里集合不是一个,而是三个嵌套,那么咱们就使用flat+map.注意到,z是嵌套集合最后一个输出,做为map的输入。
再看一个例子,假设有嵌套集合以下:
val nestedNumbers = List(List(1, 2), List(3, 4))
nestedNumbers.flatMap(x => x.map(_ * 2)
返回结果是
List[Int] = List(2, 4, 6, 8)
将两个集合折合成一个集合输出,并应用了x*2函数计算。这里_ * 2等同于(x: Int) => x * 2,下划线_表示通配上下文的任何变量或函数。是一种简单写法。
让咱们仍是围绕(x: Int) => x * 2继续展开,它表明一个有输入和输出的函数,若是咱们在另一个函数中须要用这个函数做为输入参数,那么如何定义另一个函数的方法参数呢?myfunc2(_*2)是一种hard code写法。
def myfunc2(fn: Int => Int): Int = {
fn(10)
}
这里的fn: Int => Int匹配(x: Int) => x * 2这样的抽象,固然也能够是(x: Int) => x + 2等,只要输入和输出返回都是整数便可。若是咱们运行:
myfunc2((x: Int) => x * 2)
结果是20, 而运行:
myfunc2((x: Int) => x + 2)
结果是12。
在这里,fn(10)中的10是fn输入参数,fn输出结果是根据myfunc2的输入决定的,有点相似访问者模式哦。
下面咱们尝试写本身的函数组合子:
var myfunc = (x: Int) => x * 2
val numbers = List(1, 2, 3, 4)
def ourMap(numbers: List[Int], fn: Int => Int): List[Int] = {
numbers.map((x: Int) => x * 2)
}
ourMap(numbers, myfunc(_))
结果是List[Int] = List(2, 4, 6, 8)