Scala教程–使用if-else块和匹配条件执行

前言

这是面向初学者的Scala教程的第3部分。 该博客上还有其他文章,您可以在我正在为其创建的计算语言学课程的链接页面上找到这些链接和其他资源。 此外,您可以在“ JCG Java教程”页面上找到本教程和其他教程系列。

有条件的

变量来了,变量就去了,根据输入,它们取不同的值。 我们通常需要根据这些值制定不同的行为。 例如,让我们模拟奥斯丁的一家酒吧招标,该酒吧必须确保他不给21岁以下的人喝酒。

scala> def serveBeer (customerAge: Int) = if (customerAge >= 21) println("beer") else println("water")
serveBeer: (customerAge: Int)Unit
 
scala> serveBeer(23)
beer
 
scala> serveBeer(19)
water

我们在这里所做的是标准使用条件来产生一个动作或另一个动作-在这种情况下,只打印一条消息或另一条消息。 if(…)中的表达式是一个布尔值,可以为truefalse 。 您可以通过直接执行不等式来看到这一点:

scala> 19 >= 21
res7: Boolean = false

并且这些表达式可以根据布尔值的合取和合取的标准规则进行组合。 连词以&&表示,而以||表示取 。

scala> 19 >= 21 || 5 > 2
res8: Boolean = true
 
scala> 19 >= 21 && 5 > 2
res9: Boolean = false

要检查是否相等,请使用==

scala> 42 == 42
res10: Boolean = true
 
scala> "the" == "the"
res11: Boolean = true
 
scala> 3.14 == 6.28
res12: Boolean = false
 
scala> 2*3.14 == 6.28
res13: Boolean = true
 
scala> "there" == "the" + "re"
res14: Boolean = true

等于运算符==赋值运算符=不同,如果尝试将=用于相等性测试,则会出现错误。

scala> 5 = 5
<console>:1: error: ';' expected but '=' found.
5 = 5
^
 
scala> x = 5
<console>:10: error: not found: value x
val synthvar$0 = x
^
<console>:7: error: not found: value x
x = 5
^

第一个例子是完全不好的,因为我们不能希望将值赋给像5这样的常数。在第二个例子中,错误抱怨找不到值x 。 那是因为它是一个有效的构造,假设先前已经定义了var变量x

scala> var x = 0
x: Int = 0
 
scala> x = 5
x: Int = 5

回想一下,使用var变量,可以为它们分配一个新值。 但是,实际上并不需要经常使用var,并且坚持使用val有很多优点。 在我们进行过程中,我将帮助您从这些角度进行思考。 现在,请尝试忽略该语言中存在var的事实!

回到条件。 首先,这里有更多比较运算符:

x == y(x等于y)
x!= y(x不等于y)
x> y(x大于y) x <y(x小于y) x> = y(x等于y或大于y) x <= y(x等于y或小于y)

这些运算符可用于具有自然顺序的任何类型,包括字符串。

scala> "armadillo" < "bear"
res25: Boolean = true
 
scala> "armadillo" < "Bear"
res26: Boolean = false
 
scala> "Armadillo" < "Bear"
res27: Boolean = true

显然,这不是您惯常使用的字母顺序。 而是基于ASCII字符编码。

关于Scala中的条件,一个非常美丽而有用的事情是它们返回一个值。 因此,以下是设置变量xy的值的有效方法。

scala> val x = if (true) 1 else 0
x: Int = 1
 
scala> val y = if (false) 1 else 0
y: Int = 0

这里不是那么令人印象深刻,但让我们回到调酒师,而不是使用serveBeer函数打印一个String,我们可以让它返回一个代表饮料的String,如果是21岁以上,则返回“啤酒”,否则返回“水” 。

scala> def serveBeer (customerAge: Int) = if (customerAge >= 21) "beer" else "water"
serveBeer: (customerAge: Int)java.lang.String
 
scala> serveBeer(42)
res21: java.lang.String = beer
 
scala> serveBeer(20)
res22: java.lang.String = water

请注意,第一个serveBeer函数如何返回Unit,但此函数返回String。 单位表示不返回任何值-通常,由于我们不会在这里讨论的原因而建议不要这样做。 不管怎样,上面显示的条件赋值的一般模式是您会经常使用的东西。

条件语句还可以具有多个ifelse 。 例如,假设酒保只是向每位顾客提供适合年龄的饮料,而21岁以上的人会喝啤酒,青少年喝苏打水,小孩应该喝果汁。

scala> def serveDrink (customerAge: Int) = {
|     if (customerAge >= 21) "beer"
|     else if (customerAge >= 13) "soda"
|     else "juice"
| }
serveDrink: (customerAge: Int)java.lang.String
 
scala> serveDrink(42)
res35: java.lang.String = beer
 
scala> serveDrink(16)
res36: java.lang.String = soda
 
scala> serveDrink(6)
res37: java.lang.String = juice

当然,在任何IFS 否则IFS的布尔表达式可以是复杂的连词和小表情析取。 现在让我们考虑一个面向计算语言学的示例,它可以利用这一点,并且我们将在以后的教程中继续进行构建。

每个人(希望如此)都知道词性是什么。 (如果没有,请查看YouTube上的Grammar Rock。)在计算语言学中,我们倾向于使用非常详细的标记集,而这些标记集远远超出了“名词”,“动词”,“形容词”等。 例如, 来自Penn Treebank的标签集将NN用于单数名词(表格),将NNS用于复数名词(表格),将NNP用于单数专有名词(约翰),将NNPS用作复数专有名词(维京人)。

这是一个带注释注释的句子,带有来自Penn Treebank的《华尔街日报》部分第一句的postags,格式为word / postag。

/ DT指数/ NN / IN / DT 100 / CD最大/ JJS Nasdaq / NNP金融/ JJ股票/ NNS上涨/ VBD适度/ RB as / IN well / RB ./。

我们将看到如何处理这些集体不久,但现在,让我们建立一个回合单一标签,如“NNP”转变为“NN”和“JJS”到“JJ”,使用条件语句的功能。 我们会让所有其他邮政保持原样。

我们将从一个次优的解决方案开始,然后对其进行完善。 您可能要尝试的第一件事是为每个完整表单标签创建一个案例,并输出其相应的缩短标签。

scala> def shortenPos (tag: String) = {
|     if (tag == "NN") "NN"
|     else if (tag == "NNS") "NN"
|     else if (tag == "NNP") "NN"
|     else if (tag == "NNPS") "NN"
|     else if (tag == "JJ") "JJ"
|     else if (tag == "JJR") "JJ"
|     else if (tag == "JJS") "JJ"
|     else tag
| }
shortenPos: (tag: String)java.lang.String
 
scala> shortenPos("NNP")
res47: java.lang.String = NN
 
scala> shortenPos("JJS")
res48: java.lang.String = JJ

因此,它可以完成工作,但是有很多冗余-特别是在很多情况下,返回值都是相同的。 我们可以使用析取来处理这个问题。

def shortenPos2 (tag: String) = {
  if (tag == "NN" || tag == "NNS" || tag == "NNP" || tag == "NNP") "NN"
  else if (tag == "JJ" || tag == "JJR" || tag == "JJS") "JJ"
  else tag
}

这些在逻辑上是等效的。

使用字符串的属性,有一种更简单的方法。 在这里, startsWith方法非常有用。

scala> "NNP".startsWith("NN")
res51: Boolean = true
 
scala> "NNP".startsWith("VB")
res52: Boolean = false

我们可以使用它来简化postag缩短功能。

def shortenPos3 (tag: String) = {
  if (tag.startsWith("NN")) "NN"
  else if (tag.startsWith("JJ")) "JJ"
  else tag
}

这使得添加附加条件变得非常容易,该附加条件会使所有的动词标签折叠到“ VB”。 (左为练习。)

关于条件赋值的最后一点:它们可以返回您喜欢的任何东西,因此,例如,以下内容都是有效的。 例如,这是一个(非常)简单(且非常不完美)的英语词干分析器,返回词干和后缀。

scala> def splitWord (word: String) = {
|     if (word.endsWith("ing")) (word.slice(0,word.length-3), "ing")
|     else if (word.endsWith("ed")) (word.slice(0,word.length-2), "ed")
|     else if (word.endsWith("er")) (word.slice(0,word.length-2), "er")
|     else if (word.endsWith("s")) (word.slice(0,word.length-1), "s")
|     else (word,"")
| }
splitWord: (word: String)(String, java.lang.String)
 
scala> splitWord("walked")
res10: (String, java.lang.String) = (walk,ed)
 
scala> splitWord("walking")
res11: (String, java.lang.String) = (walk,ing)
 
scala> splitWord("booking")
res12: (String, java.lang.String) = (book,ing)
 
scala> splitWord("baking")
res13: (String, java.lang.String) = (bak,ing)

如果我们想直接使用带变量的词干和后缀,则可以立即为其分配它们。

scala> val (stem, suffix) = splitWord("walked")
stem: String = walk
suffix: java.lang.String = ed

匹配

Scala提供了另一种非常强大的方法来对条件执行进行编码,称为matching 。 它们与if-else块有很多共同点,但是具有一些不错的附加功能。 我们将返回postag缩短器,从标签的完整列表以及每种情况下的操作开始,例如我们第一次尝试使用if-else。

def shortenPosMatch (tag: String) = tag match {
  case "NN" => "NN"
  case "NNS" => "NN"
  case "NNP" => "NN"
  case "NNPS" => "NN"
  case "JJ" => "JJ"
  case "JJR" => "JJ"
  case "JJS" => "JJ"
  case _ => tag
}
 
scala> shortenPosMatch("JJR")
res14: java.lang.String = JJ

注意,最后一种情况下带有下划线“ _”是默认的操作,类似于if-else块末尾的“ else”。

将此函数与之前的if-else函数shortPos进行比较,该函数在其形式“ else if(tag == “。)的定义中有很多重复。Match语句使您可以执行相同的操作,但是更加简洁明了当然,我们可以缩短时间。

def shortenPosMatch2 (tag: String) = tag match {
  case "NN" | "NNS" | "NNP" | "NNPS" => "NN"
  case "JJ" | "JJR" | "JJS" => "JJ"
  case _ => tag
}

这比之前定义的if-else shortPosMatch2更具可读性。

除了可读性之外,match语句还提供了一些逻辑保护。 例如,如果您不小心有两个重叠的案例,那么您将得到一个错误。

scala> def shortenPosMatchOops (tag: String) = tag match {
|   case "NN" | "NNS" | "NNP" | "NNPS" => "NN"
|   case "JJ" | "JJR" | "JJS" => "JJ"
|   case "NN" => "oops"
|   case _ => tag
| }
<console>:10: error: unreachable code
case "NN" => "oops"

这是一个显而易见的示例,但是具有更复杂的匹配选项,可以使您免于错误!

我们不能像使用if-else shortPosMatch3一样使用startsWith方法。 但是,我们可以在match语句中很好地使用正则表达式,我们将在以后的教程中介绍。

match语句真正发挥作用的地方在于,它们不仅可以对简单变量(如Strings和Ints)的值进行匹配,还可以进行匹配。 匹配的一种用法是检查函数的输入类型,该函数可以采用许多类型的超类型。 回想一下Any是所有类型的超类型; 如果我们有以下函数接受任何类型的参数,则可以使用匹配检查参数的类型,并相应地执行不同的行为。

scala> def multitypeMatch (x: Any) = x match {
|    case i: Int => "an Int: " + i*i
|    case d: Double => "a Double: " + d/2
|    case b: Boolean => "a Boolean: " + !b
|    case s: String => "a String: " + s.length
|    case (p1: String, p2: Int) => "a Tuple[String, Int]: " + p2*p2 + p1.length
|    case (p1: Any, p2: Any) => "a Tuple[Any, Any]: (" + p1 + "," + p2 + ")"
|    case _ => "some other type " + x
| }
multitypeMatch: (x: Any)java.lang.String
 
scala> multitypeMatch(true)
res4: java.lang.String = a Boolean: false
 
scala> multitypeMatch(3)
res5: java.lang.String = an Int: 9
 
scala> multitypeMatch((1,3))
res6: java.lang.String = a Tuple[Any, Any]: (1,3)
 
scala> multitypeMatch(("hi",3))
res7: java.lang.String = a Tuple[String, Int]: 92

因此,例如,如果它是一个Int型,我们可以做乘法之类的事情,如果它是一个布尔型,我们可以对它进行取反(用!),依此类推。 在case语句中,我们提供了一个具有匹配类型的新变量,然后在箭头=>之后,我们可以以类型安全的方式使用该变量。 稍后,我们将看到如何创建类(特别是案例类),其中定期使用这种基于匹配的函数。

同时,这是一个简单加法函数的示例,该函数允许输入一个String或Int来指定其参数。 例如,我们想要的行为是这样的:

scala> add(1,3)
res4: Int = 4
 
scala> add("one",3)
res5: Int = 4
 
scala> add(1,"three")
res6: Int = 4
 
scala> add("one","three")
res7: Int = 4

假设我们只处理拼写为1到5的版本,并且我们无法处理的任何字符串(例如“ six”和aardvark”)都被视为0。然后使用match的以下两个函数将其处理。

def convertToInt (x: String) = x match {
  case "one" => 1
  case "two" => 2
  case "three" => 3
  case "four" => 4
  case "five" => 5
  case _ => 0
}
 
def add (x: Any, y: Any) = (x,y) match {
  case (x: Int, y: Int) => x + y
  case (x: String, y: Int) => convertToInt(x) + y
  case (x: Int, y: String) => x + convertToInt(y)
  case (x: String, y: String) => convertToInt(x) + convertToInt(y)
  case _ => 0
}

就像if-else块一样,匹配项可以返回您喜欢的任何类型,包括元组,列表等。

匹配块用于许多其他有用的上下文中,我们将在后面介绍。 同时,还应该指出匹配实际上是在变量分配中使用的。 我们已经在Tuples中看到了它,但是可以通过Lists和其他类型来完成。

scala> val (x,y) = (1,2)
x: Int = 1
y: Int = 2
 
scala> val colors = List("blue","red","yellow")
colors: List[java.lang.String] = List(blue, red, yellow)
 
scala> val List(color1, color2, color3) = colors
color1: java.lang.String = blue
color2: java.lang.String = red
color3: java.lang.String = yellow

在使用Scala创建脚本时,在命令行中使用args Array的情况下,这尤其有用。 例如,考虑一个如下运行的程序。

$ scala nextYear.scala John 35
Next year John will be 36 years old.

这是我们的方法。 (将接下来的两行另存为nextYear.scala并尝试一下。)

val Array(name, age) = args
println("Next year " + name + " will be " + (age.toInt + 1) + " years old.")

注意,我们必须做age.toInt 。 这是因为age本身是字符串,而不是Int。
使用if-else块和match块进行条件执行是将复杂行为构建到您经常看到和使用的程序中的强大功能!

参考: Scala入门程序员第一步,Bcompose博客的JCG合作伙伴 Jason Baldridge的 第3部分

相关文章 :


翻译自: https://www.javacodegeeks.com/2011/09/scala-tutorial-conditional-execution.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值