文章目录

  • 高阶函数
    • 强制转换方法为函数
  • 方法嵌套
  • 多参数列表
  • 样例类
    • 比较
    • 拷贝
  • 模式匹配
    • 密封类
  • 单例对象
    • 伴生对象
  • 正则表达式模式
  • 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相关推荐

  1. Scala教程之:面向对象的scala

    文章目录 面向对象的scala Unified Types Classes Traits 面向对象的scala 我们知道Scala是一种JVM语言,可以合java无缝衔接,这也就大大的扩展了scala ...

  2. Scala教程之:可扩展的scala

    文章目录 隐式类 限制条件 字符串插值 s 字符串插值器 f 插值器 raw 插值器 自定义插值器 Scala是扩展的,Scala提供了一种独特的语言机制来实现这种功能: 隐式类: 允许给已有的类型添 ...

  3. Scala教程之:Scala基础

    文章目录 常量 变量 代码块 函数 方法 类 case类 对象 trait main方法 这篇文章我们大概过一下Scala的基础概念,后面的文章我们会有更详细的讲解Scala的具体内容. 常量 在Sc ...

  4. scala教程之:可见性规则

    文章目录 public Protected private scoped private 和 scoped protected 和java很类似,scala也有自己的可见性规则,不同的是scala只有 ...

  5. Scala教程之:深入理解协变和逆变

    文章目录 函数的参数和返回值 可变类型的变异 在之前的文章中我们简单的介绍过scala中的协变和逆变,我们使用+ 来表示协变类型:使用-表示逆变类型:非转化类型不需要添加标记. 假如我们定义一个cla ...

  6. Scala教程之:Either

    在之前的文章中我们提到了Option,scala中Option表示存在0或者1个元素,如果在处理异常的时候Option就会有很大的限制,因为Option如果返回None,那么我并不知道具体的异常到底是 ...

  7. Scala教程之:可变和不变集合

    文章目录 mutable HashMap immutable HashMap 集合在程序中是非常有用的,只有用好集合才能真正感受到该语言的魅力.在scala中集合主要在三个包里面:scala.coll ...

  8. Scala教程之:Future和Promise

    文章目录 定义返回Future的方法 阻塞方式获取Future的值 非阻塞方式获取Future的值 Future链 flatmap VS map Future.sequence() VS Future ...

  9. Scala教程之:PartialFunction

    Scala中有一个很有用的traits叫PartialFunction,我看了下别人的翻译叫做偏函数,但是我觉得部分函数更加确切. 那么PartialFunction是做什么用的呢?简单点说Parti ...

最新文章

  1. 【译】Monolith first —— Martin Fowler 对于微服务架构的看法
  2. 公司--保存时验证数据是否保存重复
  3. 深入理解r2dbc-mysql
  4. 【阿里妈妈数据科学系列】第一篇:认识在线实验
  5. cogs 547:[HAOI2011] 防线修建
  6. 你愿意一辈子当一个打工的吗
  7. [转载] Python_range()_逆序
  8. 004 Leaflet 第四个demo 使用自己的图标替换marker图标
  9. php微信投票刷票,微信投票程序源码
  10. 导出数据到txt文本
  11. C程序设计,贪吃蛇程序
  12. 如何批量打印jpg图片
  13. Python+OpenCv实现摄像头人脸识别
  14. 定义雇员类Employee
  15. Windows系统复制文件到虚拟机Linux环境的解决
  16. 360高级前端架构师Hax(贺师俊):前端开发编程语言的过去、现在和未来
  17. Pulp Fiction (低俗小说)
  18. 《计算机网络》第7版——知识摘要总结
  19. 基于Pytorch的上市公司财务预警分析
  20. JAVA计算平方,幂运算,三角函数等

热门文章

  1. html5伪类效果延缓,CSS3实现伪类hover离开时平滑过渡效果示例
  2. Windows下编译和安装Boost库
  3. Windows系统程序设计之结构化异常处理
  4. GetModuleHandle,AfxGetInstanceHandle使用区别
  5. scrapy 解决Redirecting 301 302重定向问题
  6. Golang 正在成为互联网大厂的主流编程语言!
  7. 做好项目,从正确定义问题开始!
  8. 腾讯2020校园招聘----覆盖
  9. HTTP和HTTPS总结
  10. LiveVideoStackCon深圳-AI无孔不入