文档章节

scala中的map函数与for中的yield

 如风达
发布于 2015/08/28 15:45
字数 1609
阅读 1329
收藏 0

首先我们从scala的函数开始:

  在命令后输入:(x:Int) => x * 2

  这种奇怪的格式让我们陌生,但是如果你熟悉javascript的函数如下:

function myfunc(param){

   alert("hello" + param);

}

  这是一个弹出窗口hello的函数,显示的是hellp+输入参数,这个param不只是可以传入值,也可以传入另外一个函数,为了能够传入另外一个函数作为参数,被传入的函数在写法上要改变一下,比如:

var myfunc2 = function (param){

   alert("hello" + param);

}

  可以看到,我们将函数名称移到了左边,右边就剩余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)


本文转载自:http://www.jdon.com/idea/scala/functional-programming.html

粉丝 7
博文 255
码字总数 22313
作品 0
深圳
私信 提问
Scala 的 yield 例子 (for 循环和 yield 的例子)

我看了《Programming in Scala》一书,仍然对 Scala yield 关键字的理解不甚清楚。起初我以为 Scala yield 的与 Ruby 的 yield 是一样,Ruby 中 yield 是被传入代码块的占位符。Scala 中的 ...

月下独酌100
2016/04/06
26
0
Scala 的 yield 例子 (for 循环和 yield 的例子)

我看了《Programming in Scala》一书,仍然对 Scala yield 关键字的理解不甚清楚。起初我以为 Scala yield 的与 Ruby 的 yield 是一样,Ruby 中 yield 是被传入代码块的占位符。Scala 中的 ...

Zero零_度
2016/08/16
7
0
Scala 特殊的对象和关键字

Option: 标准类库中的Option类型用样例类来表示那种可能存在、也可能不存在的值。 Option 有两个子类别,一个是 Some,一个是 None,当他回传 Some 的时候,代表这个函式成功地给了你一个 St...

BigMaN
2013/09/06
0
0
写给Python程序员的Scala入门教程

随着业务和数据的需要,我们引入了。对的支持还是挺好的,但毕竟它还是使用开发的,且现有的API并没有100%覆盖Python。所以就有了这篇文章,让Python程序员可以接触这门更高(级)、更快(速...

羊八井
2015/11/29
906
2
为Scala初学者准备的基础知识

1 前言 Scala 编程语言在设计上追求概念上的完美统一,只要理解了一个地方的设计,便会触类旁通,对其它地方的设计也会恍然大悟。但是Scala的设计哲学需要初学者付出更多的精力去学习和理解,...

joymufeng
2016/10/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

八、RabbitMQ的集群原理

集群架构 写在前面 RabbitMQ集群是按照低延迟环境设计的,千万不要跨越WAN或者互联网来搭建RabbitMQ集群。如果一定要在高延迟环境下使用RabbitMQ集群,可以参考使用Shovel和Federation工具。...

XuePeng77
今天
1
0
mac系统下,brew 安装mysql,用终端可以连接,navicat却连接不上?

问题: 1.报错? 2059 - Authentication plugin 'caching_sha2_password' cannot be loaded: dlopen(../Frameworks/caching_sha2_password.so, 2): image not found 2.自己通过设置,已经把密......

写bug的攻城狮
昨天
2
0
老生常谈,HashMap的死循环

问题 最近的几次面试中,我都问了是否了解HashMap在并发使用时可能发生死循环,导致cpu100%,结果让我很意外,都表示不知道有这样的问题,让我意外的是面试者的工作年限都不短。 由于HashMap...

群星纪元
昨天
5
0
拉普拉斯算子

拉普拉斯算子是二阶微分算子。 我们知道,一维离散信号一阶微分公式如下: 相应的,一维离散信号二阶微分公式如下: 由于图像有x和y两个方向,因此图像信号属于二维离散信号。其在x,y两个...

yepanl
昨天
3
0
记录"正则表达式"

详细请查看我的博客:https://blog.enjoytoshare.club/article/RegularExpression.html 1 写在前面 正则表达式(Regular Expression)在代码中常常简写为regex。正则表达式通常被用来检索、替...

wugenqiang
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部