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来交互编程。

  1. scala REPL提供了一个帮助系统,使用:help命令可以启动。
  2. 当命令返回值时,REPL会把它赋给一个新的常量变量,这些“res”变量会顺序编号,并且可以在后边命令中使用(例如 2*res2)
  3. 使用 :load 命令可以加载外部的scala代码并执行。
  4. 可以使用 :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!")
View Code

 

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
View Code

 

2.2 变量

变量与值类似,使用var关键字来定义。 在惯例中,能使用值的尽量使用值,否则才使用变量。

变量在命名时,使用驼峰规范

var <indentifier>[: <type>] = <data>

Scala数值类型通常包含如下一些,下边表格根据数据等级排序,低等级类型的数可以赋给高等级类型的变量。

 

 下边例子,将高等级的Double赋给低等级的Int会出错,但是低等级Int赋给Double是可以的。

所有类型都有一个toType()方法,使用该方法可以手动完成类型转换(但是会损失精度)。

 

2.3 字符串

Scala的字符串是基于Java的String的。相比与Java String,下边总结了几点不同之处:

  1. 相等操作符(==)会检查真正的相等性,而不是对象引用相等性,可以使用==比较两个字符串字面值的相等。
  2. 多行字面量,在 """ """ 中间可以包含跨行的字符串定义。
  3. 字符串内插,可以方便将变量放入字符串中。

字符串内插记法是在字符串第一个双引号前增加一个"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
View Code

 

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)
View Code

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)
View Code

 

3.表达式和条件式

表达式(expression)是返回一个值的代码单元。可以使用表达式定义值和变量,语法如下:

val <identifier>[: <type>] = <expression>

expression是一个大括号扩住的代码块,块中的最后一个表达式将作为整个表达式块的返回值。

下面,分别介绍一下Scala中的几种表达式块:

3.1 if表达式 : 

scala的if表达式与java非常类似,区别主要是:

  1. 整个if表达式可以输出返回值,并赋予一个值或者变量。
  2. 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)
View Code

 

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}
View Code

 

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
View Code

5.2 高阶函数

首先,介绍下高阶函数,类似于map()和reduce()函数,包含一个函数类型的值作为输入参数或返回值,都可以算为高阶函数。

 

 

 

 

posted @ 2020-01-22 10:18  葛洪俊  阅读(433)  评论(0)    收藏  举报