每天半小时掌握Scala(day 02)

Posted 爱上终身学习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每天半小时掌握Scala(day 02)相关的知识,希望对你有一定的参考价值。

===控制结构===

 

    scala和其他编程语言有一个根本性差异scala几乎所有构造出来的语法结构都有值这个特性使得程序结构更加精简。scala内建的控制结构很少仅有if、while、for、try、match和函数调用等而已如此之少的理由是,scala从语法层面上支持函数字面量

 

if表达式

scalaif/else语法结构与java等一样但是在scalaif/else表达式有值这个值就是跟在if/esle后边的表达式的值如下

val s = if(x > 0) 1 else -1

同时注意:scala的每个表达式都有一个类型比如上述if/esle表达式的类型是Int。如果是混合类型表达式则表达式的类型是两个分支类型的公共超类型。StringInt的超类型就是Any。如果一个if语句没有else部分则当if条件不满足时表达式结果为Unit。

if(x > 0) 1

就相当于

if(x > 0) 1 else ()

 

while循环

scala拥有与javac++中一样的whiledo-while循环,while、do-while结果类型是Unit。

 

for表达式

scala中没有类似于for(; ; )for循环你可以使用如下形式的for循环语句

for(i <- 表达式)

for表达式语法对于数组和所有集合类均有效具体介绍如下

枚举:for(i <- 1 to 10),其中“i <- 表达式语法称之为发生器该语句是让变量i(注意此处循环变量ival但无需你指定),该变量的类型是集合的元素类型遍历表达式中的所有值。1 to 10产生的Range包含上边界如果不想包含上边界可以使用until

过滤也叫守卫for表达式的发生器中使用过滤器可以通过添加if子句实现:for(i <- 1 to 10 if i!=5),如果要添加多个过滤器即多个if子句的话要用分号隔开:for(i <- 1 to 10 if i!=5; if i!=6)。

嵌套枚举如果使用多个“<-”子句你就得到了嵌套的循环”,:for(i <- 1 to 5; j <- 1 to i)。

流间变量绑定你可以在for发生器以及过滤器等中使用变量保存计算结果以便在循环体中使用从而避免多次计算以得到该结果流间变量绑定和普通变量定义相似它被当作val,但是无需声明val关键字

制造新集合:for(…) yield 变量/循环体最终将产生一个集合对象集合对象的类型与它第一个发生器的类型是兼容的

实际上:for表达式具有等价于组合应用map、flatMap、filterforeach这几种高阶函数的表达能力实际上所有的能够yield(产生结果的for表达式都会被编译器转译为高阶方法map、flatMapfilter的组合调用所有的不带yieldfor循环都会被转译为仅对高阶函数filterforeach的调用正是由于这几个高阶函数支持了for表达式所以如果一个数据类型要支持for表达式它就要定义这几个高阶函数有些时候你可以使用for表达式代替map、flatMap、filterforeach的显式组合应用或许这样会更清晰明了呢

 

scala中没有breakcontinue语句如果需要类似的功能时我们可以

1) 使用Boolean类型的控制变量

2) 使用嵌套函数你可以从函数当中return

3) ...

 

match表达式模式匹配

scala中没有switch,但有更强大的match它们的主要区别在于

任何类型的常量/变量都可以作为比较用的样本

在每个case语句最后不需要break,break是隐含的

更重要的是match表达式也有值

如果没有匹配的模式MatchError异常会被抛出

match表达式的形式选择器 match { 备选项 }一个模式匹配包含了一系列备选项每个都开始于关键字case每个备选项都包含了一个模式以及一到多个表达式它们将在模式匹配过程中被计算箭头符号“=>”隔开了模式和表达式按照代码先后顺序一旦一个模式被匹配则执行“=>”后边的表达式((这些)表达式的值就作为match表达式的值),后续case语句不再执行示例如下

a match {

case 1 => "match 1"

case _ => "match _"

}

match模式的种类如下

通配模式可以匹配任意对象一般作为默认情况放在备选项最后

case _ =>

变量模式类似于通配符可以匹配任意对象不同的是匹配的对象会被绑定在变量上之后就可以使用这个变量操作对象所谓变量就是在模式中临时生成的变量不是外部变量外部变量在模式匹配时被当作常量使用常量模式注意同一个模式变量只能在模式中出现一次

常量模式仅匹配自身任何字面量都可以作为常量外部变量在模式匹配时也被当作常量使用

case "false" => "false"

case true => "truth"

case Nil => "empty list"

对于一个符号名是变量还是常量呢?scala使用了一个简单的文字规则对此加以区分用小写字母开始的简单名被当作是模式变量所有其他的引用被认为是常量如果常量是小写命名的外部变量那么它就得特殊处理一下了如果它是对象的字段则可以加上“this.”“obj.”前缀或者更通用的是使用字面量标识符解决问题也即用反引号“`”包围之

抽取器模式抽取器机制基于可以从对象中抽取值的unapplyunapplySeq方法其中,unapply用于抽取固定数量的东东,unapplySeq用于抽取可变数量的东东它们都被称为抽取方法抽取器正是通过隐式调用抽取方法抽取出对应东东的抽取器中也可以包含可选的apply方法它也被称作注入方法注入方法使你的对象可以当作构造器来用而抽取方法使你的对象可以当作模式来用对象本身被称作抽取器与是否具有apply方法无关样本类会自动生成伴生对象并添加一定的句法以作为抽取器实际上你也可以自己定义一个任意其他名字的单例对象作为抽取器使用以这样的方式定义的抽取器对象与样本类类型是无关联的你可以对数组列表元组进行模式匹配这正是基于抽取器模式的

类型模式你可以把类型模式当作类型测试和类型转换的简易替代示例如下

case s: String => s.length

变量绑定除了独立的变量模式之外你还可以把任何其他模式绑定到变量只要简单地写上变量名一个@符号以及这个模式


模式守卫模式守卫接在模式之后,开始于if,相当于一个判断语句。守卫可以是任意的引用模式中变量的布尔表达式。如果存在模式守卫,只有在守卫返回true的时候匹配才算成功。

Option类型:scala为可选值定义了一个名为Option的标准类型一个Option实例的值要么是Some类型的实例要么是None对象分离可选值最通常的办法是通过模式匹配如下

case Some(s) => s

case None => “?”

模式无处不在scala模式可以出现在很多地方而不单单在match表达式里比如

模式使用在变量定义中如下

val myTuple = (123, “abc”)

val (number, string) = myTuple

模式匹配花括号中的样本序列(即备选项)可以用在能够出现函数字面量的任何地方实质上样本序列就是更普遍的函数字面量函数字面量只有一个入口点和参数列表样本序列可以有多个入口点每个都有自己的参数列表每个样本都是函数的一个入口点参数被模式所特化如下 

case Some(x) => "is int"

case None => "?"

}

for表达式里也可以使用模式示例如下

for((number, string) <- myTuple) println(number + string)

模式匹配中的中缀标注带有两个参数的方法可以作为中缀操作符使用使用中缀操作符时实际上是其中一个操作数在调用操作符对应的方法而另一个操作数作为方法的参数但对于模式来说规则有些不同如果被当作模式那么类似于p op q这样的中缀标注等价于op(p,q),也就是说中缀标注符op被用做抽取器模式

 

===函数()===

 

函数定义

定义函数时除了递归函数之外你可以省略返回值类型声明,scala会根据=号后边的表达式的类型推断返回值类型同时=号后边表达式的值就是函数的返回值你无需使用return语句(scala推荐你使用表达式值代替return返回值当然根据你的需要也可以显式使用return返回值)。示例如下

def abs(x: Double) = if(x >= 0) x else -x

def fac(n: Int) = {

var r = 1

for(i <- 1 to n) r = r * i

r

}

对于递归函数必须指定返回值类型如下

def fac(n: Int) : Int = if(n <= 0 ) 1 else  n * fac(n-1)

但你要知道的是声明函数返回类型总是有好处的它可以使你的函数接口清晰因此建议不要省略函数返回类型声明

函数体定义时有“=”如果函数仅计算单个结果表达式则可以省略花括号如果表达式很短甚至可以把它放在def的同一行里

去掉了函数体定义时的“=”的函数一般称之为过程”,过程函数的结果类型一定是Unit。因此有时定义函数时忘记加等号结果常常是出乎你的意料的

没有返回值的函数的默认返回值是Unit。

 

函数调用

scala方法调用的空括号可以省略惯例是如果方法带有副作用就加上括号如果没有副作用就去掉括号如果在函数定义时省略了空括号那么在调用时就不能加空括号另外函数作为操作符使用时的调用形式参见相应部分

 

函数参数

一般情况下,scala编译器是无法推断函数的参数类型的因此你需要在参数列表中声明参数的类型对于函数字面量来说根据其使用环境的不同,scala有时可以推断出其参数类型

        scala里函数参数的一个重要特征是它们都是val(这是无需声明的在参数列表里你不能显式地声明参数变量为val),不是var,所以你不能在函数里面给参数变量重新赋值这将遭到编译器的强烈反对

 

重复参数

    在scala你可以指明函数的最后一个参数是重复的从而允许客户向函数传入可变长度参数列表要想标注一个重复参数可在参数的类型之后放一个星号“*”。例如

            def echo(args: String*) = for(arg <- args) println(arg)

    这样的话,echo就可以被零至多个String参数调用在函数内部重复参数的类型是声明参数类型的数组因此,echo函数里被声明为类型“String*”args的类型实际上是Array[String]。然而如果你有一个合适类型的数组并尝试把它当作重复参数传入会出现编译错误要实现这个做法你需要在数组名后添加一个冒号和一个_*符号以告诉编译器把数组中的每个元素当作参数而不是将整个数组当作单一的参数传递给echo函数如下

echo(arr: _*)

 

默认参数命名参数

    函数的默认参数与java以及c++中相似都是从左向右结合另外你也可以在调用时指定参数名示例如下

    def fun(str: String, left: String = “[”, right: String = “]”) = left + str + right

    fun(“hello”)

    fun(“hello”, “<<<”)

    fun(“hello”, left = “<<<”) 



每天18:00向你推送最实用的干货,欢迎关注~

点击这里查看往期精彩内容:




以上是关于每天半小时掌握Scala(day 02)的主要内容,如果未能解决你的问题,请参考以下文章

每天半小时掌握Scala(day 01)

每天半小时掌握Scala(day 03)

5个能够改变一生的自学网站,每天半小时效果看的见!

linux学习之路每天半小时

每天半小时下班的公交车上,把html5学完了...

自动化快速上手--Python--元组--每天半小时