`
thomassun
  • 浏览: 27554 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Scala新手指南中文版 - 第三篇 Patterns Everywhere(模式无处不在)

阅读更多

译者注:原文出处http://danielwestheide.com/blog/2012/12/05/the-neophytes-guide-to-scala-part-3-patterns-everywhere.html,翻译:Thomas

 

在前两篇中,我花了不少时间解释了case class在模式中如何解构的,如何实现能够以任何方式提取任何类型数据的提取器。

到目前为止你只看到少数使用模式的方法,现在是时候来看看在你的Scala代码里该如何用上模式,我们开始吧!

 

模式匹配表达式

模式时常会出现的场景是在模式匹配表达式里,在参加了Coursera的Scala课程或者完整看了前两篇文章后,你应该对这种用法很熟悉了。首先有个表达式e,后面跟着match关键字和一个代码块,代码块里可以包含任意多个case子句。一个case子句由case关键字开头 后面跟着模式和一个可选的防护从句,后面跟随着模式匹配成功后要执行的一段代码。

下面是一个使用了模式和一个包含了防护从句的case的简单的例子:

 

case class Player(name: String, score: Int)

def printMessage(player: Player) = player match {
  case Player(_, score) if score > 100000 => println("Get a job, dude!")
  case Player(name, _) => println("Hey " + name + ", nice to see you again!")
}
printMessage方法的返回类型是 Unit, 它的唯一目的是执行副作用,即打印一个信息。有一点很重要,你不一定非得在这里用模式匹配,可以当成类似Java里的switch语句一样。在这里称作模式匹配表达式是有原因的,整个match的返回值是第一个被匹配的模式的返回值。

 

通常,用上它的这个特性是一个好办法,这让你可以解耦两个原本不属于一样的事情,也让你的代码更容易测试。我们可以将上面的代码重写一下:

 

def message(player: Player) = player match {
  case Player(_, score) if score > 100000 => "Get a job, dude!"
  case Player(name, _) => "Hey " + name + ", nice to see you again!"
}
def printMessage(player: Player) = println(message(player))
现在我们有了一个返回String类型的message方法。现在它是一个纯函数了,返回的是模式匹配的结果。你当然可以把结果赋给一个常量。

 

在常量/变量定义时使用模式

在Scala中还可以把模式用在常量定义语句的左侧(变量定义里当然也可以用,不过我们还是想尽量保持Scala代码的函数式风格,所以你在我的系列文章里不会看到太多的变量的使用)。假设我们有一个方法会返回当前玩家,我们先实现一个假的方式总是返回一个固定的玩家:

 

def currentPlayer(): Player = Player("Daniel", 3500)
通常你的常量定义看起来是这样的:

 

 

val player = currentPlayer()
doSomethingWithTheName(player.name)
如果你是个Python程序员,也许对序列解包功能很熟悉。Scala提供类似的功能,你可以在常量定义语句的左侧使用任何模式,我们来将上面的代码改造一下,让它可以结构player后再赋值给指定常量:

 

 

val Player(name, _) = currentPlayer()
doSomethingWithTheName(name)
你可以拿任意模式用作此用途,不过通常你得要确保模式总是被匹配的,否则你就会面临运行时异常。例如,下面的例子就会有问题。scores是一个返回得分列表的方法,在下面的代码中,这方法如果返回的是空列表就会导致问题:

 

 

def scores: List[Int] = List()
val best :: rest = scores
println("The score of our champion is " + best)
看,程序丢出来一个MatchError。结果就是我们的游戏程序跑不下去了,因为没有获取到游戏成绩。

 

想要这样来使用模式,最好的方法是结构case class,你可以在编译时就能确定它们的类型。如果同时还使用了tuple的话,可以让你的代码可读性更好。假设我们有一个函数返回一个tuple包装的玩家的名字和他的得分,不再使用Player类了:

 

def gameResult(): (String, Int) = ("Daniel", 3500)

 

访问tuple的元素看上去会让人感觉迷惑:

 

val result = gameResult()
println(result._1 + ": " + result._2)
这时在常量定义时用模式来结构会是安全的,因为我们清楚我们面对的是一个Tuple2数据类型:

 

 

val (name, score) = gameResult()
println(name + ": " + score)
这比前一段代码更可读,不是吗?

 

 

在for语句中使用模式

在for语句中使用模式也很有价值。举例来说,for语句里可以包含常量的定义。前面介绍的在常量定义语句的左侧使用模式的用法在for语句里也适用。如果我们想在我们的游戏程序里实现名人堂-即得分超过一个阈值的玩家名单。我们可以用for语句实现一段可读性强的代码:

 

def gameResults(): Seq[(String, Int)] =
  ("Daniel", 3500) :: ("Melissa", 13000) :: ("John", 7000) :: Nil

def hallOfFame = for {
  result <- gameResults()
  (name, score) = result
  if (score > 5000)
} yield name

 

hallOfFame的结果会是List("Melissa", "John"), 因为第一个玩家没有满足守卫子句的条件。

我们还可以进一步精简一下代码,在for语句里,generator的左侧也可以是模式。所以我们可以不用引进result常量,可以直接在generator左侧用模式来解构:

 

def hallOfFame = for {
   (name, score) <- gameResults()
  if (score > 5000)
} yield name
在这里例子里,模式(name,score)总是匹配的,所以在没有守卫子句if (score > 5000)时,这段代码就相当于把tuple转成玩家名字 。

 

还有一点很重要,在generator的左侧的模式还兼具过滤功能 - 如果左侧模式不匹配,相应的元素就会被过滤掉。举例来说明,假设我们有一个列表的列表,我们想要返回所有非空列表的大小。这意味着我们必须过滤掉空list,然后返回留下的列表的大小。下面是一种解决方式:

val lists = List(1, 2, 3) :: List.empty :: List(5, 3) :: Nil

for {
  list @ head :: _ <- lists
} yield list.size

generator左侧的模式不匹配空list。它不会抛出MatchError,而是会过滤掉空list,所以结果就会返回List(3,2)。在for语句中使用模式是一种非常自然和强大的组合,使用一段Scala后,你会爱上它们的。

匿名函数

最后,模型还可以用来定义匿名函数。如果你在Scala里用过catch语句块来处理异常,那么其实你已经用过这种用法了。模式匹配匿名函数是一个需要单独篇章来讲解的题目,因为有太多东西要说了,所以我就不会在这个关于模式的篇章里涉及太多,而会在本系列的后篇来介绍。

更新:修复一个hallOfFame 中的错误。感谢Rajiv帮忙找到错误.

作者:Daniel Westheide,2012.12.5

 

 

 

分享到:
评论

相关推荐

    scala-xml_2.12-1.0.6-API文档-中文版.zip

    包含翻译后的API文档:scala-xml_2.12-1.0.6-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.scala-lang.modules:scala-xml_2.12:1.0.6; 标签:scala、lang、modules、xml、中文文档、jar包、java; 使用方法...

    scala-compiler-2.12.7-API文档-中文版.zip

    包含翻译后的API文档:scala-compiler-2.12.7-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.scala-lang:scala-compiler:2.12.7; 标签:scala、lang、compiler、中文文档、jar包、java; 使用方法:解压翻译...

    flink-scala_2.12-1.14.3-API文档-中文版.zip

    包含翻译后的API文档:flink-scala_2.12-1.14.3-javadoc-API文档-中文(简体)版.zip 对应Maven信息:groupId:org.apache.flink,artifactId:flink-scala_2.12,version:1.14.3 使用方法:解压翻译后的API文档,用...

    scala-intellij-bin-2016.3.9

    "scala-intellij-bin-2016.3.9"是针对Scala语言的一个特定版本的IntelliJ IDEA插件,该版本为2016.3.9。这个插件是专门为Scala开发者设计的,旨在提高他们在IntelliJ IDEA中的开发体验。 Scala是一种多范式编程语言...

    scala-intellij-bin-2020.2.3.zip

    - **社区支持**:IntelliJ IDEA拥有庞大的开发者社区,提供丰富的第三方插件和资源。 总之,"scala-intellij-bin-2020.2.3.zip" 提供了在IntelliJ IDEA中进行高效Scala开发所需的工具集。通过使用这个插件,开发者...

    scala-intellij-bin-2016.3.1.zip

    在函数式编程方面,Scala提供了高阶函数、柯里化、模式匹配、不可变数据结构等特性,这些在IntelliJ IDEA的Scala插件中都有良好的支持。通过IDE,开发者可以方便地使用这些功能,提升代码的简洁性和可读性。 对于...

    scala-compiler-2.11.8-API文档-中英对照版.zip

    包含翻译后的API文档:scala-compiler-2.11.8-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven坐标:org.scala-lang:scala-compiler:2.11.8; 标签:scala、lang、compiler、中英对照文档、jar包、java; 使用...

    scala-parser-combinators-2.11-1.0.4-API文档-中文版.zip

    标签:11、parser、scala、combinators_2、lang、modules、jar包、java、API文档、中文版; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构...

    scala-SDK-4.7.0-vfinal-2.12-linux.gtk.x86_64.tar.gz

    scala-SDK-4.7.0-vfinal-2.12-linux.gtk.x86_64.tar.gz scala-SDK-4.7.0-vfinal-2.12-linux.gtk.x86_64.tar.gz

    scala-intellij-bin-2018.3.2.zip

    scala-intellij-bin-2018.3.2.zip插件,亲测可用!!!scala-intellij-bin-2018.3.2.zip插件,亲测可用!!!scala-intellij-bin-2018.3.2.zip插件,亲测可用!!!

    scala-reflect-2.11.8-API文档-中英对照版.zip

    包含翻译后的API文档:scala-reflect-2.11.8-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven坐标:org.scala-lang:scala-reflect:2.11.8; 标签:reflect、scala、lang、jar包、java、API文档、中英对照版; ...

    scala插件 scala-intellij-bin-2018.3.5.zip scala-intellij-bin-2018.3.6.zip

    `scala-intellij-bin-2018.3.5.zip` 和 `scala-intellij-bin-2018.3.6.zip` 是两个版本的Scala插件,分别适用于IntelliJ IDEA的2018.3.5和2018.3.6版本。这些插件是为了增强IDE对Scala语言的支持,提供代码高亮、...

    scala-intellij-bin-2017.2.13

    Scala是一种强大的多范式编程语言,它融合了面向对象和函数式编程的特性。IntelliJ IDEA是一款广受赞誉的Java集成开发环境(IDE),它为开发者提供了丰富的工具和功能来提升开发效率。"Scala-intellij-bin-2017.2.13...

    scala-intellij-bin-0.41

    Scala是一种强大的多范式编程语言,它融合了函数式编程和面向对象编程的特点。IntelliJ IDEA是一款广受赞誉的Java开发集成环境,为开发者提供了高效、智能的代码编写体验。"scala-intellij-bin-0.41"是专门为...

    scala-intellij-bin-2017.2.6

    Scala是一种强大的多范式编程语言,它融合了面向对象和函数式编程的概念。IntelliJ IDEA是一款广受欢迎的集成开发环境(IDE),尤其在Java和...如果你是Scala新手或希望提升开发效率,安装这个插件将是一个明智的选择。

    scala-ide-plugin-eclipse

    scala eclipse插件.对应scala版本:2.10--2.11,对应eclipes版本:4.4--4.5. update site:http://download.scala-ide.org/sdk/lithium/e44/scala211/stable/site 下载地址:...

    scala-intellij-bin-2019.1.2.zip

    此外,该插件还支持Scala的模式匹配、匿名函数、特质等特性,使得IDE能更好地理解和处理这种高度抽象的语言。 代码重构是软件开发中的重要环节,Scala插件提供了诸如提取方法、重命名变量、移动类等重构操作,确保...

    scala-compiler-2.11.12-API文档-中文版.zip

    包含翻译后的API文档:scala-compiler-2.11.12-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.scala-lang:scala-compiler:2.11.12; 标签:scala、lang、compiler、中文文档、jar包、java; 使用方法:解压...

    scala-xml_2.11-1.0.1-API文档-中文版.zip

    包含翻译后的API文档:scala-xml_2.11-1.0.1-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.scala-lang.modules:scala-xml_2.11:1.0.1; 标签:11、xml_2、scala、lang、modules、jar包、java、中文文档; ...

    scala-intellij-bin-2021.3.6.zip

    2. **模式匹配**:Scala的模式匹配功能使得处理数据结构和解析数据变得更加简单,它可以与case类和枚举类型一起使用,实现强大的数据解构。 3. **函数式编程**:Scala支持高阶函数和闭包,可以定义匿名函数,使用...

Global site tag (gtag.js) - Google Analytics