0
0

Scala 魔法函数

鸟窝 发表于 2016年01月04日 15:44 | Hits: 1728
Tag: Scala

Scala有一些语法糖,让一些特定名称的函数拥有一些特殊的能力。这些语法糖并没有专门的文档介绍,只是散落在不同的文章和教程中。本文整理里这些魔法函数,并通过例子演示它们的功能。

apply, unapply

当类或对象有一个主要用途的时候,apply方法为你提供了一个很好的语法糖。比如a是一个对象,a.apply(x)则可以简化为a(x)
unapply方法是抽取器(Extractor),有人也翻译成提取器,经常用在模式匹配上。

1234567891011
val foo = Foo(1)foo match {  case Foo(x) => println(x)}class Foo(val x: Int) {}object Foo {  def apply(x: Int) = new Foo(x)  def unapply(f: Foo) = Some(f.x)}

我们一般都在伴生对象内定义apply,unapply方法,但是Trait,class内都可以定义这些方法。

update

与apply方法类似,update也是一个魔法方法。对于一个实例a, Scala可以将a.update(x,y)简化为a(x)=y。

1234567891011121314
val bar = Bar(10)bar(0) = 1println(bar(0))class Bar(n :Int) {  val a = Array[Int](n)  def apply(n :Int) = a(n)  def update(n:Int, v:Int) = a(n) = v}object Bar {  def apply(n :Int) = new Bar(n)}

val bar = Bar(10)调用伴生对象的apply方法生成一个Bar的实例。bar(0) = 1等价于bar.update(0,1)。println(bar(0))等价于println(bar.apply(0)),也就是class Bar中定义的apply方法。

unapplySeq

类似unapply,用来从对象中抽取序列,常用在模式匹配上:

123456789101112
val m = M(1)m match {  case M(n1,others@_*) => println(others)}class M {}object M {  def apply(a: Int*): M = new M  def unapplySeq(m: M): Option[Seq[Int]] = Some(1 :: 2 :: 3 :: Nil)}

identifier_=

如果你只是简单的字段操作,只需定义一个var类型的属性即可。但是如果你在set的时候执行额外的逻辑,比如参数检查等,你就可能需要setter符号。
Scala为setter提供了特殊的语法:

1234567891011121314151617
val obj = new Xobj.x = -1println(obj.x)obj.x = 10println(obj.x)class X {  var _x = 0  //setter  def x_=(n: Int) {    if (n < 0) _x = 0 else _x = n  }  //getter  def x = _x}

上面的例子中x_=就是x的setter。

一元操作符

1234
unary_+unary_-unary_!unary_~

如果你想重载/实现一元操作符(+,-,!,~),可以定义上面的方法:

12345678910111213141516
val x = new X('小')println(!x)x.s = '黑'println(!x)class X(var s: Char) {  val s1 = "大小多少长短胖瘦高矮黑白冷暖"  def unary_! = {    var i = s1 indexOf s    if (i % 2 == 0) i = i + 1 else i = i - 1    new X(s1(i))  }  override def toString = "X(" + s + ")"}

op=

如果你定义了一个操作符op,那么A <op>= B能自动转换为A = A <op> B。

12345678910111213
var s = new X("abc")s ::/ 1println(s)s ::/= 1println(s)class X(var s: String) {  def ::/(n:Int) = {    new X(s + n)  }  override def toString = "X(" + s + ")"}

操作符不能是字母和数字(alphanumeric),也不能是!=, ==, <= 或者 >=。

Dynamic

Dynamic对象可以调用不存在的字段或者方法,Scala将它们转换为下面的四个方法:

1234
selectDynamicupdateDynamicapplyDynamic applyDynamicNamed

这有点动态语言的风格。

例子:

12345678910111213141516171819
val d = new DynImpl  println(d.foo)  d.foo = 10  println(d.ints(1, 2, 3))  println(d.ints(i1 = 1, i2 = 2, 3))  class DynImpl extends Dynamic {    var map = Map.empty[String, Any]    def selectDynamic(name: String) = name    def updateDynamic(name: String)(value: Any) {      map += name -> value    }    def applyDynamic(name: String)(args: Any*) = s"method '$name' called with arguments ${args.mkString("'", "', '", "'")}"    def applyDynamicNamed(name: String)(args: (String, Any)*) = s"method '$name' called with arguments ${args.mkString("'", "', '", "'")}"  }

_*

_*可以用作模式匹配任意数量的参数:

123456
case class A(n: Int*)val a = A(1, 2, 3, 4, 5)a match {  case A(1, 2, _*) =>}

你可以将可变参数绑定到一个值上:

123
a match {  case A(1, 2, as @_*) => println(as)}

另外_*还可以作为辅助类型描述:

12
val l = 1 :: 2 :: 3 :: Nilval a2 = A(l :_*)

这里:_*用来将集合作为可变参数传递。

@Pattern Binders

Scala规范8.1.3定义了模式绑定的定义。上面的例子as @_*也演示了这个功能。下面再看个例子:

123456789
sealed abstract class Exprcase class Number(num: Double) extends Exprcase class UnOp(operator: String, arg: Expr) extends Exprval expr = UnOp("abs", Number(-1))expr match {  case UnOp("abs", e @ Number(_)) => println(e)  case _ =>}

参考资料

  1. http://stackoverflow.com/questions/1483212/list-of-scalas-magic-functions
  2. http://stackoverflow.com/questions/15799811/how-does-type-dynamic-work-and-how-to-use-it
  3. http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#pattern-binders
转载请注明文章来源: colobu.com

原文链接: http://colobu.com/2016/01/04/Scala-magic-functions/

0     0

我要给这篇文章打分:

可以不填写评论, 而只是打分. 如果发表评论, 你可以给的分值是-5到+5, 否则, 你只能评-1, +1两种分数. 你的评论可能需要审核.

评价列表(0)