Scala基本语法与概念笔记

来源:互联网 发布:弱肉强食知乎 编辑:程序博客网 时间:2024/06/04 17:49

转自:   http://zionyun.github.io/dev/2014/05/09/scala-basics/  感谢原作者!


Scala基本语法与概念笔记

Partial application 和 Currying

和大部分函数式语言一样,Scala也支持partial application和currying。在应用函数时,你可以只提供部分的实际参数,而另外一部分参数则使用_来代替,这样会得到另外一个函数。_实际上起到一个匿名通配符 (unnamed wildcard) 的作用。

def adder(m: Int, n: Int) = m + n               //> adder: (m: Int, n: Int)Intval add2 = adder(2, _: Int)                     //> add2  : Int => Int = <function1>val add3 = adder(_: Int, 3)                     //> add3  : Int => Int = <function1>

而Currying则是说一个函数能够接受多个函数参数列表。从本质上,这样的函数可以通过使一个函数将另外一个函数作为返回值来实现。以下面为例,最外层的函数sum接受一个函数作为参数,并返回sumInternal作为结果。而在sumInternal的实现是依赖于sum的函数参数的。实际调用sum时的例子为sum((x: Int) => x * x)(0, 2)

def sum(f: Int => Int): (Int, Int) => Int = {  def sumInternal(a: Int, b: Int) => Int = {if (a > b) 0else f(a) + sumInternal(a + 1, b)  }  sumInternal}

按照这个的模式,我们可以用过嵌套定义函数来实现任意多的函数列表,只是略显繁琐。Scala提供了语法糖来简化Currying函数的定义。

def productSum(m: Double, x: Double)(n: Double, y: Double) = (m * x) + (n * y)

Partial application和Curring也是可以混合使用的。在multiplyTwoSum里我们对productSum两个参数列表都只传入了部分参数,而且得到的结果是(Double, Double) => Double,即是一个接受两个Double类型参数的函数。注意这和oneMultiplySum的区别:oneMultiplySum接受一个Int类型的参数,并返回一个(Double, Double) => Double`的函数作为结果。使用Partial application和Curring时,必须仔细观察得到的函数签名。我们也可以对一个partially applied的函数使用curried方法,得到它的curried版本。

val multiplyTwoSum = productSum(_: Double, 2)(_: Double, 2)                                              //> multiplyTwoSum  : (Double, Double) => Double = <function2>val fourteen = multiplyTwoSum(3, 4)             //> fourteen  : Double = 14.0val oneMultiplySum = productSum(1, _: Int) _    //> oneMultiplySum  : Int => ((Double, Double) => Double) = <function1>val curriedAdder = (adder _).curried            //> curriedAdder  : Int => (Int => Int) = <function1>val five = curriedAdder(2)(3)                   //> five  : Int = 5

变长参数 (Variable length parameters)

Scala支持某个类型的变长参数,只要在函数参数的类型后面加上*就可以了。变长参数的类型是Seq[T],因此大部分数据结构都能作为实际参数传入这样的函数中。

def capitalizeAll(args: String*) = {  args.map { arg => arg.capitalize }}

类 (Classes)

和Java或者C#不同,Scala里类的构造函数 (constructor) 并不是一个专门的方法 (所以构造函数这个翻译对于Scala的语境来说还挺不合适的,构造器可能更加适合一点?),而是指类定义里面除去方法定义 (method definitions) 的所有部分。

// classesclass Calculator(val brand: String) {//define method with defdef add(m: Double, n: Double) = m + ndef getBrand = brand// define fields with val (or var)val color: String =  if (brand == "HP") {    "black"  } else {    "white"  }}val hpCalculator = new Calculator("HP")         //> hpCalculator  : basics.Calculator = basics$$anonfun$main$1$Calculator$1@664

抽象类,继承和重载都很正常。对于抽象类里只声明但是没有实现的定义,在实现时直接定义就可以。如果在抽象类里已经实现了的定义,那在子类里重新实现时要求使用override关键字。

// classes// Inheritanceclass ScientificCalculator(brand: String) extends Calculator(brand) {  def log(m: Double, base: Double) = math.log(m) / math.log(base)}// overloading methodsclass AdvancedScientificCalculator(brand: String) extends ScientificCalculator(brand) {  def log(m: Int): Double = log(m, math.exp(1))  override val color: String = "Golden"}// abstract classes; no implementation; can't create instances from abstract classabstract class Shape {  def getArea(): Int}

Traits

Traits定义了一系列的数据和方法,并且可以用来扩展或者mixin在你的类定义中。Traits和抽象类的特点和使用范围都有很相似的地方,所以选择的时候需要考虑它们不同的特点。一个类能够扩展多个traits,但是只能扩展一个父类。而traits并不能接受构造参数,而抽象类可以。

  trait Car {    val brand: String  }  trait Sport {    val Engine: String  }  class BMW extends Car with Sport {    val brand = "BWM"    val Engine = "Turbo"  }

类型参数

定义类,函数,和traits时,都可以通过方括号[]引入类型参数。

  trait Cache[K, V] {    def get(key: K): V    def put(key: K, value: V)    def delete(key: K)    def remove[K](key: K)  }

apply 方法

Scala可以让你为类或者对象定义apply方法,来重载()运算符。

  class Foo {}  object FooMaker {    def apply() = new Foo  }  val newFoo = FooMaker()  class Bar {    def apply() = 0  }  val bar = new Bar  bar()                                           //> res0: Int = 0

Scala里面类和对象能够使用同一个名称。所以通常我们会为一个类定义一个同名的对象 (companion object),作为该类型的factories。

  class DummyClass(val foo: String) {  }  object DummyClass { // Classes and Objects can have the same name.    def apply(foo: String) = new DummyClass(foo)  }  val c = DummyClass("foo")                                          //> res0: Int = 0

函数即对象

Scala里面的函数其实是一系列traits的集合。以一个Int => Int类型的函数为例,它扩展了Function1[Int, Int]这个trait。而且这个trait定义了apply函数,因为你可以用调用函数的语法来使用这个对象。实际上,Scala的提供了语法糖,让用户可以用Int => Int来表示Function1[Int, Int]

注意这里的函数并不包括类里面定义的方法。

  object addOne extends Function1[Int, Int] {  // can be class AddOne extends (Int => Int)    def apply(m: Int): Int = m + 1  }

EOF


Reference

  • Twitter Scala School



0 0
原创粉丝点击