Scala教程之:函数式的Scala(一)

简介: Scala教程之:函数式的Scala(一)

文章目录



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 + 32
  def 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) accumulator
      else 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.Random
val 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 Notification
case class Email(sender: String, title: String, body: String) extends Notification
case class SMS(caller: String, message: String) extends Notification
case 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))  // 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.screenOff
  case c: Computer => c.screenSaverOn
}


相关文章
|
分布式计算 Java Scala
一天学完spark的Scala基础语法教程十三、文件IO操作(idea版本)
一天学完spark的Scala基础语法教程十三、文件IO操作(idea版本)
111 0
一天学完spark的Scala基础语法教程十三、文件IO操作(idea版本)
|
分布式计算 Java Scala
一天学完spark的Scala基础语法教程十二、异常处理(idea版本)
一天学完spark的Scala基础语法教程十二、异常处理(idea版本)
192 0
一天学完spark的Scala基础语法教程十二、异常处理(idea版本)
|
分布式计算 Java Scala
一天学完spark的Scala基础语法教程十一、正则表达式(idea版本)
一天学完spark的Scala基础语法教程十一、正则表达式(idea版本)
147 0
一天学完spark的Scala基础语法教程十一、正则表达式(idea版本)
|
存储 分布式计算 Java
一天学完spark的Scala基础语法教程十、类和对象(idea版本)
一天学完spark的Scala基础语法教程十、类和对象(idea版本)
119 0
一天学完spark的Scala基础语法教程十、类和对象(idea版本)
|
分布式计算 Scala Spark
一天学完spark的Scala基础语法教程九、迭代器(idea版本)
一天学完spark的Scala基础语法教程九、迭代器(idea版本)
125 0
一天学完spark的Scala基础语法教程九、迭代器(idea版本)
|
分布式计算 Scala Spark
一天学完spark的Scala基础语法教程八、集合(idea版本)
一天学完spark的Scala基础语法教程八、集合(idea版本)
139 0
一天学完spark的Scala基础语法教程八、集合(idea版本)
|
存储 分布式计算 Scala
一天学完spark的Scala基础语法教程七、数组(idea版本)
一天学完spark的Scala基础语法教程七、数组(idea版本)
127 0
一天学完spark的Scala基础语法教程七、数组(idea版本)
|
分布式计算 Scala Spark
一天学完spark的Scala基础语法教程六、字符串(idea版本)-2
一天学完spark的Scala基础语法教程六、字符串(idea版本)
127 0
一天学完spark的Scala基础语法教程六、字符串(idea版本)-2
|
分布式计算 Java Scala
一天学完spark的Scala基础语法教程六、字符串(idea版本)-1
一天学完spark的Scala基础语法教程六、字符串(idea版本)
112 0
一天学完spark的Scala基础语法教程六、字符串(idea版本)-1
|
分布式计算 Scala Spark
一天学完spark的Scala基础语法教程五、闭包(idea版本)
一天学完spark的Scala基础语法教程五、闭包(idea版本)
82 0
一天学完spark的Scala基础语法教程五、闭包(idea版本)