Scala是一门函数式语言,接下来我们会讲一下几个概念:
- 高阶函数
- 方法嵌套
- 多参数列表
- 样例类
- 模式匹配
- 单例对象
- 正则表达式模式
- For表达式
高阶函数
高阶函数通常来讲就是函数的函数,也就是说函数的输出参数是函数或者函数的返回结果是函数。在Scala中函数是一等公民。
我们看一下Scala集合类(collections)的高阶函数map:
<code>val
salaries = Seq(20000
,70000
,40000
)val
doubleSalary = (x:Int
) => x *2
val
newSalaries = salaries.map(doubleSalary) /<code>
map接收一个函数为参数。所以map是一个高阶函数,map也可直接接收一个匿名函数,如下所示:
<code>val salaries = Seq(20000, 70000, 40000)
val newSalaries = salaries.map(x =>
x
*2
) // List(40000
,140000
,80000
) /<code>
在上面的例子中,我们并没有显示使用x:Int的形式,这是因为编译器可以通过类型推断推断出x的类型,对其更简化的形式是:
<code>val salaries = Seq(20000, 70000, 40000)
val newSalaries = salaries.map(_
*2
) /<code>
既然Scala编译器已经知道了参数的类型(一个单独的Int),你可以只给出函数的右半部分,不过需要使用_代替参数名(在上一个例子中是x)
强制转换方法为函数
如果你传入一个方法到高阶函数中,scala会将该方法强制转换成函数,如下所示:
<code>case class WeeklyWeatherForecast(temperatures: Seq[Double]
) { private def convertCtoF(temp: Double) = temp *1.8
+32
def forecastInFahrenheit: Seq[Double]
= temperatures.map(convertCtoF) // /<code>
在这个例子中,方法convertCtoF被传入forecastInFahrenheit。这是可以的,因为编译器强制将方法convertCtoF转成了函数x => convertCtoF(x) (注: x是编译器生成的变量名,保证在其作用域是唯一的)。
方法嵌套
在Scala的方法中可以嵌套方法,如下所示:
<code>def
factorial
(x: Int)
: Int = {def
fact
(x: Int, accumulator: Int)
: Int = {if
(x <=1
) accumulatorelse
fact(x -1
, x * accumulator) } fact(x,1
) } println("Factorial of 2: "
+ factorial(2
)) println("Factorial of 3: "
+ factorial(3
)) /<code>
程序输出为:
<code>Factorial of 2:
2
Factorial of 3:
6
/<code>
多参数列表
Scala和java不同的是他可以定义多个参数列表,下面是一个例子:
<code>def foldLeft[B
](z: B
)(op: (B, A) => B): B /<code>
可以看到该方法定义了两个参数列表, z是初始值,op是一个二元运算,下面是它的一个调用:
<code>val
numbers
=
List(1,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
)
val
res
=
numbers.foldLeft(0)((m,
n)
=>
m
+
n)
print(res)
//
55
/<code>
利用scala的类型推断,我们可以让代码更加简洁:
<code>numbers
.foldLeft
(0
)(_ + _) /<code>
样例类
case class主要用于不可变的数据。他们和普通类几乎是一样的。
<code>caseclass
Book
(isbn: String)val
frankenstein = Book("978-0486282114"
) /<code>
实例化案例类的时候并不需要new关键字,因为case class有一个默认的apply方法来负责对象的创建。
在case class中,参数是public并且val的,这意味着case class的参数不可变:
<code>case
class
Message(sender:String
, recipient:String
, body:String
) val message1 = Message(, ,"Ça va ?"
) println(message1.sender) message1.sender = /<code>
这里message1.sender不能被重新赋值,因为他是val(不可变)的。
比较
case class的比较是按值比较的,而不是按引用:
<code>caseclass
Message
(sender: String, recipient: String, body: String)val
message2 = Message(, ,"Com va?"
)val
message3 = Message(, ,"Com va?"
)val
messagesAreTheSame = message2 == message3 /<code>
虽然上面是不同的对象,但是因为他们的值相同,所以最后的比较是true。
拷贝
可以使用copy来做case class的浅拷贝。
<code>case
class
Message(sender:String
, recipient:String
, body:String
) val message4 = Message(, ,"Me zo o komz gant ma amezeg"
) val message5 = message4.copy(sender = message4.recipient, recipient = ) message5.sender message5.recipient message5.body /<code>
模式匹配
scala中使用match关键字和case来做模式匹配,类似java中的switch。
下面是一个简单的模式匹配的例子:
<code>import scala.util.Random val x: Int = Random.nextInt(10
) x match {case
0
=>"zero"
case
1
=>"one"
case
2
=>"two"
case
_ =>"other"
} /<code>
最后一个case _表示匹配其余所有情况。
match表达式是有值的,如下所示:
<code>def matchTest(x: Int):String
= x match {case
1
=>"one"
case
2
=>"two"
case
_
=>"other"
} matchTest(3
) matchTest(1
) /<code>
case也可以匹配case class, 如下所示:
<code>abstract
class
Notificationcase
class
Email(sender:String
, title:String
, body:String
)extends
Notificationcase
class
SMS(caller:String
, message:String
)extends
Notificationcase
class
VoiceRecording(contactName:String
, link:String
)extends
Notification def showNotification(notification: Notification):String
= { notification match {case
Email(sender, title, _) => s"You got an email from $sender with title: $title"
case
SMS(number
, message) => s"You got an SMS from $number! Message: $message"
case
VoiceRecording(name, link) => s"you received a Voice Recording from $name! Click the link to hear it: $link"
} } val someSms = SMS("12345"
,"Are you there?"
) val someVoiceRecording = VoiceRecording("Tom"
,"voicerecording.org/id/123"
) println(showNotification(someSms)) println(showNotification(someVoiceRecording)) /<code>
case后面还可以加if语句,我们称之为模式守卫。
<code>def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String
]):String
= { notification match {case
Email(sender, _, _)if
importantPeopleInfo.contains(sender) =>"You got an email from special someone!"
case
SMS(number
, _)if
importantPeopleInfo.contains(number
) =>"You got an SMS from special someone!"
case
other
=> showNotification(other) } } /<code>
也可以只做类型匹配:
<code>abstract
class
Devicecase
class
Phone(model:String
)extends
Device { def screenOff ="Turning screen off"
}case
class
Computer(model:String
)extends
Device { def screenSaverOn ="Turning screen saver on..."
} def goIdle(device: Device) = device match {case
p:Phone
=> p.screenOffcase
c:Computer
=> c.screenSaverOn } /<code>
密封类
特质(trait)和类(class)可以用sealed标记为密封的,这意味着其所有子类都必须与之定义在相同文件中。
<code>sealedabstract
class
Furniturecase
class
Couch()extends
Furniturecase
class
Chair()extends
Furniture def findPlaceToSit(piece: Furniture):String
= piece match {case
a:Couch
=>"Lie on the couch"
case
b:Chair
=>"Sit on the chair"
} /<code>
单例对象
单例对象是一种特殊的类,可以使用关键字object来表示。单例对象是延时创建的,只有当他被第一次使用的时候才会创建。
<code>package
loggingobject
Logger { def info(message: String):Unit
= println(s"INFO:
$message
") } /<code>
单例对象的一个作用就是定义功能性方法,可以在任何地方被使用,如上例中的info方法。可以像如下的方式使用:
<code>import
logging.Logger.infoclass
Project
(name: String, daysToComplete:Int
)class
Test
{val
project1 = new Project("TPS Reports"
,1
)val
project2 = new Project("Website redesign"
,5
) info("Created projects"
) } /<code>
伴生对象
伴生对象是指与某个类名相同的单例对象,类和它的伴生对象可以互相访问其私有成员。下面是一个伴生对象的例子:
<code>import
scala.math._ caseclass
Circle
(radius:Double
) {import
Circle._ def area:Double
= calculateArea(radius) }object
Circle {private
def calculateArea(radius:Double
):Double
= Pi * pow(radius,2.0
) }val
circle1 = Circle(5.0
) circle1.area /<code>
伴生对象circle1可以访问类中定义的area.
注意:类和它的伴生对象必须定义在同一个源文件里。
正则表达式模式
在Scala中,可以使用.r方法将任意字符串变成一个正则表达式。如下所示:
<code>import
scala.util.matching.Regex val numberPattern:Regex
="[0-9]"
.r numberPattern.findFirstMatchIn("awesomepassword"
) match {case
Some
(_
) =>println
("Password OK"
)case
None
=>println
("Password must contain a number"
) } /<code>
你还可以使用括号来同时匹配多组正则表达式。
<code>import
scala.util.matching.Regex val keyValPattern: Regex ="([0-9a-zA-Z-#() ]+): ([0-9a-zA-Z-#() ]+)"
.r val input: String ="""background-color: #A03300; |background-image: url(img/header100.png); |background-position: top center; |background-repeat: repeat-x; |background-size: 2160px 108px; |margin: 0; |height: 108px; |width: 100%;"""
.stripMarginfor
(patternMatch "key: ${patternMatch.group(1)} value: ${patternMatch.group(2)}") /<code>
For表达式
在Scala中for循环是和yield一起使用的,他的形式是for (enumerators) yield e。 此处 enumerators 指一组以分号分隔的枚举器。这里的enumerator 要么是一个产生新变量的生成器,要么是一个过滤器。for 表达式在枚举器产生的每一次绑定中都会计算 e 值,并在循环结束后返回这些值组成的序列。 如下所示:
<code>case
class
User(name:String
, age: Int) val userBase = List(User("Travis"
,28
), User("Kelly"
,33
), User("Jennifer"
,44
), User("Dennis"
,23
)) val twentySomethings =for
(user if (user.age >=20
&& user.age30
))yield
user.name twentySomethings.foreach(name
=> println(name)) /<code>
下面是一个更加复杂的例子:
<code>def
foo
(n: Int, v: Int
) =for
(i 0 until n; j if i + j == v)yield
(i, j) foo(10
,10
)foreach
{case
(i, j) => println(s"($i, $j) "
) } /<code>
你可以在使用 for 表达式时省略 yield 语句。此时会返回 Unit。
欢迎关注我的公众号:程序那些事,更多精彩等着您!
更多内容请访问:flydean的博客 flydean.com