Scala教程之:函数式的Scala
文章目录
- 高阶函数
- 强制转换方法为函数
- 方法嵌套
- 多参数列表
- 样例类
- 比较
- 拷贝
- 模式匹配
- 密封类
- 单例对象
- 伴生对象
- 正则表达式模式
- For表达式
Scala是一门函数式语言,接下来我们会讲一下几个概念:
- 高阶函数
- 方法嵌套
- 多参数列表
- 样例类
- 模式匹配
- 单例对象
- 正则表达式模式
- For表达式
高阶函数
高阶函数通常来讲就是函数的函数,也就是说函数的输出参数是函数或者函数的返回结果是函数。在Scala中函数是一等公民。
我们看一下Scala集合类(collections)的高阶函数map:
val salaries = Seq(20000, 70000, 40000)
val doubleSalary = (x: Int) => x * 2
val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)
map接收一个函数为参数。所以map是一个高阶函数,map也可直接接收一个匿名函数,如下所示:
val salaries = Seq(20000, 70000, 40000)
val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
在上面的例子中,我们并没有显示使用x:Int的形式,这是因为编译器可以通过类型推断推断出x的类型,对其更简化的形式是:
val salaries = Seq(20000, 70000, 40000)
val newSalaries = salaries.map(_ * 2)
既然Scala编译器已经知道了参数的类型(一个单独的Int),你可以只给出函数的右半部分,不过需要使用_代替参数名(在上一个例子中是x)
强制转换方法为函数
如果你传入一个方法到高阶函数中,scala会将该方法强制转换成函数,如下所示:
case class WeeklyWeatherForecast(temperatures: Seq[Double]) {private def convertCtoF(temp: Double) = temp * 1.8 + 32def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- passing the method convertCtoF
}
在这个例子中,方法convertCtoF被传入forecastInFahrenheit。这是可以的,因为编译器强制将方法convertCtoF转成了函数x => convertCtoF(x) (注: x是编译器生成的变量名,保证在其作用域是唯一的)。
方法嵌套
在Scala的方法中可以嵌套方法,如下所示:
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))
程序输出为:
Factorial of 2: 2
Factorial of 3: 6
多参数列表
Scala和java不同的是他可以定义多个参数列表,下面是一个例子:
def foldLeft[B](z: B)(op: (B, A) => B): B
可以看到该方法定义了两个参数列表, z是初始值,op是一个二元运算,下面是它的一个调用:
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
利用scala的类型推断,我们可以让代码更加简洁:
numbers.foldLeft(0)(_ + _)
样例类
case class主要用于不可变的数据。他们和普通类几乎是一样的。
case class Book(isbn: String)val frankenstein = Book("978-0486282114")
实例化案例类的时候并不需要new关键字,因为case class有一个默认的apply方法来负责对象的创建。
在case class中,参数是public并且val的,这意味着case class的参数不可变:
case class Message(sender: String, recipient: String, body: String)
val message1 = Message("guillaume@quebec.ca", "jorge@catalonia.es", "Ça va ?")println(message1.sender) // prints guillaume@quebec.ca
message1.sender = "travis@washington.us" // this line does not compile
这里message1.sender不能被重新赋值,因为他是val(不可变)的。
比较
case class的比较是按值比较的,而不是按引用:
case class Message(sender: String, recipient: String, body: String)val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?")
val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?")
val messagesAreTheSame = message2 == message3 // true
虽然上面是不同的对象,但是因为他们的值相同,所以最后的比较是true。
拷贝
可以使用copy来做case class的浅拷贝。
case class Message(sender: String, recipient: String, body: String)
val message4 = Message("julien@bretagne.fr", "travis@washington.us", "Me zo o komz gant ma amezeg")
val message5 = message4.copy(sender = message4.recipient, recipient = "claire@bourgogne.fr")
message5.sender // travis@washington.us
message5.recipient // claire@bourgogne.fr
message5.body // "Me zo o komz gant ma amezeg"
模式匹配
scala中使用match关键字和case来做模式匹配,类似java中的switch。
下面是一个简单的模式匹配的例子:
import scala.util.Randomval x: Int = Random.nextInt(10)x match {case 0 => "zero"case 1 => "one"case 2 => "two"case _ => "other"
}
最后一个case _表示匹配其余所有情况。
match表达式是有值的,如下所示:
def matchTest(x: Int): String = x match {case 1 => "one"case 2 => "two"case _ => "other"
}
matchTest(3) // other
matchTest(1) // one
case也可以匹配case class, 如下所示:
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 Notificationdef 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)) // prints You got an SMS from 12345! Message: Are you there?println(showNotification(someVoiceRecording)) // you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123
case后面还可以加if语句,我们称之为模式守卫。
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) // nothing special, delegate to our original showNotification function}
}
也可以只做类型匹配:
abstract class Device
case 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
}
密封类
特质(trait)和类(class)可以用sealed标记为密封的,这意味着其所有子类都必须与之定义在相同文件中。
sealed abstract class Furniture
case class Couch() extends Furniture
case class Chair() extends Furnituredef findPlaceToSit(piece: Furniture): String = piece match {case a: Couch => "Lie on the couch"case b: Chair => "Sit on the chair"
}
单例对象
单例对象是一种特殊的类,可以使用关键字object来表示。单例对象是延时创建的,只有当他被第一次使用的时候才会创建。
package loggingobject Logger {def info(message: String): Unit = println(s"INFO: $message")
}
单例对象的一个作用就是定义功能性方法,可以在任何地方被使用,如上例中的info方法。可以像如下的方式使用:
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") // Prints "INFO: Created projects"
}
伴生对象
伴生对象是指与某个类名相同的单例对象,类和它的伴生对象可以互相访问其私有成员。下面是一个伴生对象的例子:
import scala.math._case class 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
伴生对象circle1可以访问类中定义的area.
注意:类和它的伴生对象必须定义在同一个源文件里。
正则表达式模式
在Scala中,可以使用.r方法将任意字符串变成一个正则表达式。如下所示:
import scala.util.matching.Regexval numberPattern: Regex = "[0-9]".rnumberPattern.findFirstMatchIn("awesomepassword") match {case Some(_) => println("Password OK")case None => println("Password must contain a number")
}
你还可以使用括号来同时匹配多组正则表达式。
import scala.util.matching.Regexval keyValPattern: Regex = "([0-9a-zA-Z-#() ]+): ([0-9a-zA-Z-#() ]+)".rval 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 <- keyValPattern.findAllMatchIn(input))println(s"key: ${patternMatch.group(1)} value: ${patternMatch.group(2)}")
For表达式
在Scala中for循环是和yield一起使用的,他的形式是for (enumerators) yield e。 此处 enumerators 指一组以分号分隔的枚举器。这里的enumerator 要么是一个产生新变量的生成器,要么是一个过滤器。for 表达式在枚举器产生的每一次绑定中都会计算 e 值,并在循环结束后返回这些值组成的序列。 如下所示:
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 <- userBase if (user.age >=20 && user.age < 30))yield user.name // i.e. add this to a listtwentySomethings.foreach(name => println(name)) // prints Travis Dennis
下面是一个更加复杂的例子:
def foo(n: Int, v: Int) =for (i <- 0 until n;j <- i until n if i + j == v)yield (i, j)foo(10, 10) foreach {case (i, j) =>println(s"($i, $j) ") // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5)
}
你可以在使用 for 表达式时省略 yield 语句。此时会返回 Unit。
更多精彩内容且看:
- 区块链从入门到放弃系列教程-涵盖密码学,超级账本,以太坊,Libra,比特币等持续更新
- Spring Boot 2.X系列教程:七天从无到有掌握Spring Boot-持续更新
- Spring 5.X系列教程:满足你对Spring5的一切想象-持续更新
- java程序员从小工到专家成神之路(2020版)-持续更新中,附详细文章教程
更多教程请参考 flydean的博客
Scala教程之:函数式的Scala相关推荐
- Scala教程之:面向对象的scala
文章目录 面向对象的scala Unified Types Classes Traits 面向对象的scala 我们知道Scala是一种JVM语言,可以合java无缝衔接,这也就大大的扩展了scala ...
- Scala教程之:可扩展的scala
文章目录 隐式类 限制条件 字符串插值 s 字符串插值器 f 插值器 raw 插值器 自定义插值器 Scala是扩展的,Scala提供了一种独特的语言机制来实现这种功能: 隐式类: 允许给已有的类型添 ...
- Scala教程之:Scala基础
文章目录 常量 变量 代码块 函数 方法 类 case类 对象 trait main方法 这篇文章我们大概过一下Scala的基础概念,后面的文章我们会有更详细的讲解Scala的具体内容. 常量 在Sc ...
- scala教程之:可见性规则
文章目录 public Protected private scoped private 和 scoped protected 和java很类似,scala也有自己的可见性规则,不同的是scala只有 ...
- Scala教程之:深入理解协变和逆变
文章目录 函数的参数和返回值 可变类型的变异 在之前的文章中我们简单的介绍过scala中的协变和逆变,我们使用+ 来表示协变类型:使用-表示逆变类型:非转化类型不需要添加标记. 假如我们定义一个cla ...
- Scala教程之:Either
在之前的文章中我们提到了Option,scala中Option表示存在0或者1个元素,如果在处理异常的时候Option就会有很大的限制,因为Option如果返回None,那么我并不知道具体的异常到底是 ...
- Scala教程之:可变和不变集合
文章目录 mutable HashMap immutable HashMap 集合在程序中是非常有用的,只有用好集合才能真正感受到该语言的魅力.在scala中集合主要在三个包里面:scala.coll ...
- Scala教程之:Future和Promise
文章目录 定义返回Future的方法 阻塞方式获取Future的值 非阻塞方式获取Future的值 Future链 flatmap VS map Future.sequence() VS Future ...
- Scala教程之:PartialFunction
Scala中有一个很有用的traits叫PartialFunction,我看了下别人的翻译叫做偏函数,但是我觉得部分函数更加确切. 那么PartialFunction是做什么用的呢?简单点说Parti ...
最新文章
- 【译】Monolith first —— Martin Fowler 对于微服务架构的看法
- 公司--保存时验证数据是否保存重复
- 深入理解r2dbc-mysql
- 【阿里妈妈数据科学系列】第一篇:认识在线实验
- cogs 547:[HAOI2011] 防线修建
- 你愿意一辈子当一个打工的吗
- [转载] Python_range()_逆序
- 004 Leaflet 第四个demo 使用自己的图标替换marker图标
- php微信投票刷票,微信投票程序源码
- 导出数据到txt文本
- C程序设计,贪吃蛇程序
- 如何批量打印jpg图片
- Python+OpenCv实现摄像头人脸识别
- 定义雇员类Employee
- Windows系统复制文件到虚拟机Linux环境的解决
- 360高级前端架构师Hax(贺师俊):前端开发编程语言的过去、现在和未来
- Pulp Fiction (低俗小说)
- 《计算机网络》第7版——知识摘要总结
- 基于Pytorch的上市公司财务预警分析
- JAVA计算平方,幂运算,三角函数等