Scala介绍一 Scala核心
日期: 2020-01-22
《Scala学习手册》 Jason Swartz
1.概述和安装
Scala是“可扩展语言”的缩写(SCAlable Language),于2003创建,为JVM平台上的函数式编程以及面向对象编程提供一个高性能开发环境。所以,Scala要求使用java运行时库。并且scala是编译型静态类型语言。
在mac环境使用HomeBrew来安装scala比较方便(需要前置安装Java),直接输入命令:
$ java -version
$ brew install scala
直接输入scala就可以启动scala REPL来交互编程。
- scala REPL提供了一个帮助系统,使用:help命令可以启动。
- 当命令返回值时,REPL会把它赋给一个新的常量变量,这些“res”变量会顺序编号,并且可以在后边命令中使用(例如 2*res2)
- 使用 :load 命令可以加载外部的scala代码并执行。
- 可以使用 :paste -raw 命令,并按回车键之后,粘贴外部的scala代码在REPL中执行。在mac terminal中尝试过无法退出粘贴模式不建议使用这种方法,使用load文件的方式更好。
$ scala Welcome to Scala 2.13.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_172). Type in expressions for evaluation. Or try :help. scala> scala> println("Hello World!") Hello World! scala> 5*7 res2: Int = 35 scala> 2*res2 res3: Int = 70 scala> :help All commands can be abbreviated, e.g., :he instead of :help. :completions <string> output completions for the given string :edit <id>|<line> edit history :help [command] print this summary or command-specific help :history [num] show the history (optional num is commands to show) :h? <string> search the history :imports [name name ...] show import history, identifying sources of names :implicits [-v] show the implicits in scope :javap <path|class> disassemble a file or class name :line <id>|<line> place line(s) at the end of history :load <path> interpret lines in a file :paste [-raw] [path] enter paste mode or paste a file :power enable power user mode :quit exit the interpreter :replay [options] reset the repl and replay all previous commands :require <path> add a jar to the classpath :reset [options] reset the repl to its initial state, forgetting all session entries :save <path> save replayable session to a file :sh <command line> run a shell command (result is implicitly => List[String]) :settings <options> update compiler options, if possible; see reset :silent disable/enable automatic printing of results :type [-v] <expr> display the type of an expression without evaluating it :kind [-v] <type> display the kind of a type. see also :help kind :warnings show the suppressed warnings from the most recent line which had any scala> "Hello World!" res4: String = Hello World! scala> (22.5 * 9/5) + 32 res5: Double = 72.5 scala> res5/2 res6: Double = 36.25 scala> :load /Users/natty/Develop/Learning/scala/LearningScala/Hello.scala args: Array[String] = Array() Loading /Users/natty/Develop/Learning/scala/LearningScala/Hello.scala... Hello file world! scala> :paste -raw // Entering paste mode (ctrl-D to finish) println("Hello file world!")
2.变量和类型
2.1 值
scala主要包含值和变量。
值: 不可变、有类型的存储单元。
变量:可变、有类型的存储单元。
定义值的语法图如下:
val <indentifier>[: <type>] = <data>
值的类型,是可选项,如果不指定type,scala编译器会格局所赋的数据来推导它的类型。因为值是不可变的,当尝试给一个值赋新的值时,编译器报错。同时,当指定类型与赋的值的类型不一样时(赋值与指定类型不兼容),也会报错。
scala> val myInt:Int = 98 myInt: Int = 98 scala> val myChar:Char = 'd' myChar: Char = d scala> myInt = 16 ^ error: reassignment to val scala> val myVal:Int = "Jack" ^ error: type mismatch; found : String("Jack") required: Int
2.2 变量
变量与值类似,使用var关键字来定义。 在惯例中,能使用值的尽量使用值,否则才使用变量。
变量在命名时,使用驼峰规范。
var <indentifier>[: <type>] = <data>
Scala数值类型通常包含如下一些,下边表格根据数据等级排序,低等级类型的数可以赋给高等级类型的变量。

下边例子,将高等级的Double赋给低等级的Int会出错,但是低等级Int赋给Double是可以的。
所有类型都有一个toType()方法,使用该方法可以手动完成类型转换(但是会损失精度)。
2.3 字符串
Scala的字符串是基于Java的String的。相比与Java String,下边总结了几点不同之处:
- 相等操作符(==)会检查真正的相等性,而不是对象引用相等性,可以使用==比较两个字符串字面值的相等。
- 多行字面量,在 """ """ 中间可以包含跨行的字符串定义。
- 字符串内插,可以方便将变量放入字符串中。
字符串内插记法是在字符串第一个双引号前增加一个"s"前缀,然后以美元符号($)指示外部数据的引用。
也是使用printf记法,在这种记法中可以设定每个数值的保留位数等操作,这时需要增加一个"f"前缀。
scala> val helloStr = "Hello, world" helloStr: String = Hello, world scala> val helloCombineStr = "Hello,"+" world" helloCombineStr: String = Hello, world scala> val strMatched = (helloStr == helloCombineStr) strMatched: Boolean = true scala> val multilineStr = """ | "address":"浦东新区 洪桥景区内", | "alias":"[]", | "callCount":1336, | """ multilineStr: String = " "address":"浦东新区 洪桥景区内", "alias":"[]", "callCount":1336, " scala> val approx = 355/113f approx: Float = 3.141593 scala> val strInsert = s"Pi using 355/113f, is ${approx}" strInsert: String = Pi using 355/113f, is 3.141593 scala> strInsert res1: String = Pi using 355/113f, is 3.141593
2.4 其他内容
2.4.1 正则表达式:
String类提供了一些正则表达式的操作,其中常见的几个操作包括 matches , replaceAll ,replaceFirst 。
正则表达式语法:
val <Regex value>(<identifier>) = <input String>
其中 <Regex value> 是调用String类的r操作符转换得到的正则表达式类型。
2.4.2 元组:
元组(Tuple)是一个包含两个或者多个值的有序容器。
创建方法:
(<value 1>,<value 2>[,<value 3>...])
元组中的元素可以是不同类型的值,在进行元素访问时,访问第一个从1开始,如果要创建大小为2的元组 ,可以使用关系操作符 (->)。
scala> val info = (3, "Oppo", true) info: (Int, String, Boolean) = (3,Oppo,true) scala> info._2 res4: String = Oppo scala> val place = "Hubei" -> "Wuhan" place: (String, String) = (Hubei,Wuhan)
2.4.3 Scala类型概述:
2.5 练习
scala> 22.5*9 res0: Double = 202.5 scala> res0/5 res1: Double = 40.5 scala> res1 + 32 res2: Double = 72.5 scala> res2.toInt res3: Int = 72 scala> val appendx = 2.7255 appendx: Double = 2.7255 scala> val ed = "$" ed: String = $ scala> f"You owe me $ed${appendx}%.2f" res19: String = You owe me $2.73 scala> val result:Boolean = (false == false) result: Boolean = true scala> val input = "Frank,123 Main,925-555-1943,95122" input: String = Frank,123 Main,925-555-1943,95122 scala> val pattern = """.*,(\d{3}-\d{3}-\d{4}),.*""".r pattern: scala.util.matching.Regex = .*,(\d{3}-\d{3}-\d{4}),.* scala> val pattern(tele) = input tele: String = 925-555-1943 scala> val pattern1 = """.*,(\d{3})-(\d{3})-(\d{4}),.*""".r pattern1: scala.util.matching.Regex = .*,(\d{3})-(\d{3})-(\d{4}),.* scala> val pattern1(num1,num2,num3) = input num1: String = 925 num2: String = 555 num3: String = 1943 scala> (num1.toInt, num2.toInt, num3.toInt) res22: (Int, Int, Int) = (925,555,1943)
3.表达式和条件式
表达式(expression)是返回一个值的代码单元。可以使用表达式定义值和变量,语法如下:
val <identifier>[: <type>] = <expression>
expression是一个大括号扩住的代码块,块中的最后一个表达式将作为整个表达式块的返回值。
下面,分别介绍一下Scala中的几种表达式块:
3.1 if表达式 :
scala的if表达式与java非常类似,区别主要是:
- 整个if表达式可以输出返回值,并赋予一个值或者变量。
- scala只支持一个if和可选的else块,不能识别else if块(如果想完成该功能,只能嵌套了)。
if表达式语法:
if (<Boolean expression>) <expression>
else <expression>
3.2 匹配(Match)表达式 :
相比if表达式,匹配表达式更具表述性、语法更简洁。match语句语法:
<expression> match {
case <pattern match> => <expression>
[case ...]
}
在case语句中,可以匹配多个pattern,语法如下:
case <pattern1> | <pattern2> .. => <one or more expressions>
在多个case来做模式匹配时,如果所有case语句都不匹配,scala会抛出异常,为了避免发生异常,可以使用一个通配符来兜底(类似else语句)。通配模式匹配有两种方式:分别是值绑定和通配符(_)。
值绑定: case <identifier> => <one or more expressions>
通配符:case _ => <one or more expressions>
使用值绑定时,可以把匹配表达式的输入绑定到一个局部值,然后在case块的体中使用。但是,通配符则不可以。
在值匹配中,可以增加if语句进一步筛选,只有满足if后的boolean expression,才真正执行匹配,语法如下(注意:if后的小括号可加可不加):
case <pattern> if [(] <Boolean expression> [)] => <one or more expressions>
匹配表达式还有一种匹配类型,即匹配输入表达式的类型,语法如下:
case <identifier> : <type> => <one or more expressions>
3.3 循环:
Scala中的最重要的循环类别是for循环,为了介绍for循环,需要先介绍称为Range(范围)的新数据结构。
Range数据结构定义语法:
<starting integer> [to|until] <ending integer> [by increment]
其中,to和until的区别主要是: to要包含尾部的<ending integer>,而until则不包含尾部的<ending integer>。
for循环定义语法:
for (<identifier> <- <iterator>) [yield] [<expression>]
<iterator>就可以是上边介绍到的Range数据。yield关键字可选,如果指定了yield,调用的所有表达式的返回值将作为一个集合返回。
对于所有的循环,每次迭代可以增加一个条件判断,满足才执行后续expression,称为迭代器哨卫(也就是Boolean expression返回true时才执行):
for (<identifier> <- <iterator> if <Boolean expression>) [yield] [<expression>]
另外,可以在循环过程中,增加一个临时值或者变量,这些值/变量在迭代局部范围有效:
for (<identifier> <- <iterator> ; <identifiey> = <expression>) [yield] [<expression>]
3.4 练习:
练习代码:
//练习1:非空返回字符串,否则返回 "n/a" scala> val gr_test:String = "Juventus" gr_test: String = Juventus scala> val gs_test:String = null gs_test: String = null val my_str = gr_test match { case null => "n/a"; case _ => "Kobe" } val my_str = gs_test match { case null => "n/a" case _ => "Kobe" } val my_str = gr_test match { case null => "n/a"; case other => "Kobe " + other } //练习2:两种方式实现和0的大小比较逻辑(if语句和match语句) val approx:Double = -5.62 if (approx > 0) "greater" else if(approx == 0) "same" else "less" approx match{ case s if s>0 => "greater" case s if s==0 => "same" case s if s<0 => "less" } //练习3: 字符串转十六进制字符串 //练习4: 从1到100,每行包含一组5个数。 val sb:StringBuilder = new StringBuilder(); for(i <- 1 to 100) { if ( i == 100 ) sb.append(i) else if (i%5 ==0 ) sb.append(i + "," + "\n") else sb.append(i + "," + " ") } sb.toString() //练习5:从1到100 打印,3的倍数,5的倍数,3和5共同倍数处理成特殊的字符串 for (i <- 1 to 100){ if (i%3 ==0 && i%5 ==0) println("typesafe") else if(i%3 == 0) println("type") else if(i%5 == 0) println("safe") else println(i.toString) } //练习6: 处理练习5,简化处理成一行代码。 for(i <- 1 to 100; pow = if(i%3 == 0 && i%5 ==0) "typesafe" else if(i%3==0) "type" else if(i%5==0) "safe" else i) println(pow)
4.函数
函数是可重用逻辑的核心构件。函数式编程特别强调高可重用、可组合的函数。可以将单用途的函数调用结合在一起构成操作链(类似UNIX的管道符号连接的命令)。
函数由 输入、输出(也就是函数类型) ; 函数名; 函数体(表达式+表达式块)共同构成。
函数定义语法:
def <identifier> (<identifier> : <type>[, ... ]):<type> = <expression>
函数当然可以是无参数的,语法定义:
def <identifier>()[:<type>] = <expression>
在调用函数时,如果使用小括号需要输入给函数预订的数据类型,如果使用大括号,可以输入一个表达式块。例如下边的例子:
def calCircleMeasure(radius:Double) = { val pi:Double = math.Pi; 2*pi*(math.pow(radius,2)) } calCircleMeasure{val sum=2; sum*4}
5.首类函数
5.1 函数类型和值
函数的类型是输入类型和返回值类型的组合,由一个箭头从输入类型指向输出类型。
([<type>, ... ]) => <type>
下面例子定义了一个从Int => Int的简单函数,这里使用到了函数的类型。
在将一个“函数”赋给一个值或者变量时,通常需要指定这个值/变量的类型(函数的输入和输出),为了简便,也可以使用通配符为函数赋值:
val <indentifier> = <function name> _
scala> def double(x:Int):Int = x*2 double: (x: Int)Int scala> val myDouble1:Int=>Int = double myDouble1: Int => Int = $$Lambda$852/1513968288@7a0f1f9d scala> val myDouble2:(Int) => Int = double myDouble2: Int => Int = $$Lambda$853/4677559@41db6e4b scala> myDouble1(65) res8: Int = 130 scala> myDouble2(65) res9: Int = 130 scala> val myDouble3 = double ^ error: missing argument list for method double Unapplied methods are only converted to functions when a function type is expected. You can make this conversion explicit by writing `double _` or `double(_)` instead of `double`. scala> val myDouble3 = double _ myDouble3: Int => Int = $$Lambda$856/1753822100@3a89f2a0 scala> myDouble3(8) res10: Int = 16
5.2 高阶函数
首先,介绍下高阶函数,类似于map()和reduce()函数,包含一个函数类型的值作为输入参数或返回值,都可以算为高阶函数。

浙公网安备 33010602011771号