美国第16任总统。
亚伯拉罕·林肯曾经说过:“由于我们的案子是新的,因此我们必须重新考虑并采取行动”。
在软件工程中,事情可能不像内战和废除奴隶制那样剧烈,但是我们有关于“案例”的有趣逻辑概念。
在Java中,case语句提供了一些有限的条件分支。
在Scala中,可以使用case / match构造构造一些非常复杂的模式匹配逻辑,这不仅带来新的可能性,而且带来实现新可能性的新型思维。
让我们从经典的1年级计算机科学家庭作业开始:一个斐波那契数列,该数列不是以0、1开头,而是以1、1开头。所以该序列看起来像是:1、1、2、3、5、8 ,13,… 每个数字都是前两个数字的和 。
在Java中,我们可以这样做:
public int fibonacci(int i) {
if (i < 0)
return 0;
switch(i) {
case 0:
return 1;
case 1:
return 1;
default:
return fibonacci(i-1) + fibonacci(i - 2);
}
}
一切顺利。 如果传入了0
,它将算作系列中的第一个元素,因此应返回1
。 注意:为聚会增加更多趣味性并使事情变得更有趣,我添加了一点逻辑,如果将负数传递给我们的fibonacci方法,则返回0
。
在Scala中,为了实现相同的行为,我们将执行以下操作:
def fibonacci(in: Int): Int = {
in match {
case n if n <= 0 => 0
case 0 | 1 => 1
case n => fibonacci(n - 1) + fibonacci(n- 2)
}
}
关键点:
- 递归方法fibonacci的返回类型是
Int
。 递归方法必须明确指定返回类型(请参阅: Odersky –在Scala中编程 –第2章)。 - 可以使用
|
测试一行中的多个值。 符号。 我这样做是为了在示例的第4行上为0和1返回1。 - 不需要多个
return
语句。 在Java中,必须使用多个return
语句或多个break
语句。 - 模式匹配是一个始终返回某些内容的表达式。
- 在这个例子中,我使用了一个警卫来检查一个负数,如果它是负数,则返回零。
- 在Scala中,还可以检查不同的类型。 也可以使用通配符
_
表示法。 我们并没有在斐波那契使用任何一个,只是为了说明这些功能……def multitypes(in: Any): String = in match { case i:Int => 'You are an int!' case 'Alex' => 'You must be Alex' case s:String => 'I don't know who you are but I know you are a String' case _ => 'I haven't a clue who you are' }
模式匹配可以与Scala Maps一起使用,以达到有用的效果。 假设我们有一张地图来捕获我们认为应该在澳大利亚狮队系列赛的狮线后排各个位置上比赛的人。 地图的关键点将是后排中的位置,而相应的值将是我们认为应该在那儿玩的玩家。 为了代表橄榄球运动员,我们使用
案例类 。 现在,您是Java Heads,可以将case类视为以极其简洁的方式编写的不可变的POJO,它们也可以是可变的,但现在认为不可变。
case class RugbyPlayer(name: String, country: String);
val robKearney = RugbyPlayer('Rob Kearney', 'Ireland');
val georgeNorth = RugbyPlayer('George North', 'Wales');
val brianODriscol = RugbyPlayer('Brian O'Driscol', 'Ireland');
val jonnySexton = RugbyPlayer('Jonny Sexton', 'Ireland');
val benYoungs = RugbyPlayer('Ben Youngs', 'England');
// build a map
val lionsPlayers = Map('FullBack' -> robKearney, 'RightWing' -> georgeNorth,
'OutsideCentre' -> brianODriscol, 'Outhalf' -> jonnySexton, 'Scrumhalf' -> benYoungs);
// Note: Unlike Java HashMaps Scala Maps can return nulls. This achieved by returing
// an Option which can either be Some or None.
// So, if we ask for something that exists in the Map like below
println(lionsPlayers.get('Outhalf'));
// Outputs: Some(RugbyPlayer(Jonny Sexton,Ireland))
// If we ask for something that is not in the Map yet like below
println(lionsPlayers.get('InsideCentre'));
// Outputs: None
在此示例中,除了中心内线之外,我们每个位置都有球员,我们无法下定决心。 Scala Maps允许将空值存储为值。 现在在我们的情况下,我们实际上并没有在center中存储null。 因此,不是在center内部返回null(就像我们使用Java HashMap那样会发生),而是返回None
类型。
对于后行中的其他位置,我们具有匹配的值,并且返回Some
类型,该类型环绕相应的RugbyPlayer。 (注意: Some
和Option
从Option
扩展)。 我们可以编写一个函数,该函数与 HashMap的返回值进行模式匹配 ,并向我们返回一些更加用户友好的东西。
def show(x: Option[RugbyPlayer]) = x match {
case Some(rugbyPlayerExt) => rugbyPlayerExt.name // If a rugby player is matched return its name
case None => 'Not decided yet ?' //
}
println(show(lionsPlayers.get('Outhalf'))) // outputs: Jonny Sexton
println(show(lionsPlayers.get('InsideCentre'))) // Outputs: Not decided yet
这个例子不仅说明了模式匹配,还提出了另一种概念,即提取 。 匹配时,将提取橄榄球播放器并将其分配给rugbyPlayerExt
。 然后,我们可以从rugbyPlayerExt
获取橄榄球运动员名称的值。 实际上,我们还可以添加防护并围绕某些逻辑进行更改。 假设我们有偏见的记者(
斯蒂芬·琼斯(Stephen Jones ),他不希望任何爱尔兰球员加入球队。 他可以实施自己偏见的职能来检查爱尔兰球员
def biasedShow(x: Option[RugbyPlayer]) = x match {
case Some(rugbyPlayerExt) if rugbyPlayerExt.country == 'Ireland' =>
rugbyPlayerExt.name + ', don't pick him.'
case Some(rugbyPlayerExt) => rugbyPlayerExt.name
case None => 'Not decided yet ?'
}
println(biasedShow(lionsPlayers.get('Outhalf'))) // Outputs Jonny... don't pick him
println(biasedShow(lionsPlayers.get('Scrumhalf'))) // Outputs Ben Youngs
模式匹配集合
Scala还为集合提供了一些强大的模式匹配功能。 这是获取列表长度的简单示例。
def length[A](list : List[A]) : Int = list match {
case _ :: tail => 1 + length(tail)
case Nil => 0
}
假设我们想从一个元组中解析参数……
def parseArgument(arg : String, value: Any) = (arg, value) match {
case ('-l', lang) => setLanguage(lang)
case ('-o' | '--optim', n : Int) if ((0 < n) && (n <= 3)) => setOptimizationLevel(n)
case ('-h' | '--help', null) => displayHelp()
case bad => badArgument(bad)
}
单参数功能
考虑一个从1到10的数字列表。filter方法采用单个参数函数,该函数返回
true
false
。 单个参数函数可以应用于列表中的每个元素,并且将为每个元素返回true
或false
。 返回true
的元素将被过滤; 返回false
的元素将被过滤出结果列表。
scala> val myList = List(1,2,3,4,5,6,7,8,9,10)
myList: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> myList.filter(x => x % 2 ==1)
res13: List[Int] = List(1, 3, 5, 7, 9)
现在现在现在,听听并记住这一点。 模式可以传递给采用单个参数函数的任何方法 。 与其传递总是返回true或false的单个参数函数,我们可以使用始终返回true或false的模式。
scala> myList.filter {
| case i: Int => i % 2 == 1 // odd number will return false
| case _ => false // anything else will return false
| }
res14: List[Int] = List(1, 3, 5, 7, 9)
以后用吗?
Scala将模式编译为PartialFunction
。 这意味着不仅可以将Scala模式表达式传递给其他函数,还可以将它们存储起来以备后用。
scala> val patternToUseLater = : PartialFunction[String, String] = {
| case 'Dublin' => 'Ireland'
| case _ => 'Unknown'
}
这个例子说的是patternToUseLater
是一个部分函数,它接受一个字符串并返回一个字符串。 默认情况下,返回函数中的最后一个状态,因为case表达式是部分函数,它将作为部分函数返回并分配给pattenrToUseLater
,后者当然可以在以后使用它。
最后, 约翰尼·塞克斯顿 ( Johnny Sexton)是一位出色的橄榄球运动员,听到他要离开伦斯特(Leinster)感到遗憾。 显然,由于Sexton忙碌的日程安排,我们不能确定Johnny是否正在阅读此博客,但是如果Johnny是他,Johnny很抱歉看到您离开,我们祝您一切顺利,并希望您有一天能回到Blue Jersey。
参考: Scala模式匹配:新思维的案例? 从我们的JCG合作伙伴 Alex Staveley在都柏林的技术博客博客中获得。
翻译自: https://www.javacodegeeks.com/2013/01/scala-pattern-matching-a-case-for-new-thinking.html