索引标志符中置、一元、赋值操作符优先级结合性apply和update方法提取器(L2)带单个参数或无参数的提取器(L2)unapplySeq方法(L2)
原文发表于:http://nerd-is.in/2013-08/scala-learning-operators/
本章介绍如何实现自定以操作符。操作符和隐式转换通常用来构建领域特定语言(DSL)。本章还会介绍apply、update和unapply等特殊方法。
标志符
原来标志符是支持Unicode的…虽然我真的见过用中文名作为变量名的代码,但是我一向以来学到的都是:标志符,可以包含字母数字下划线(偶尔也能用$之类的),不能以数字开头。如果能用Unicode字符作为标志符,那么真是各种奇葩标志符都有可能出现了吧。我还是用着最传统的就好。
Scala中,标志符的选择比Java更多。详细的,我觉得不了解也是件好事,反正真正适合用来写代码的,就是键盘上那么几个键。了解一下,心里有个底,以后看见什么奇葩人物写了奇葩名字别太过惊讶就好。
Scala中,可以使用反引号`(一般在1左边那个)包含一些不能作为标志符的字符串,来使用它们。比如说关键字什么的。这只是类似于保命手段,请尽量避免使用。
Thread.`yield`() // yield是关键字,所以需要用`包起来
中置、一元、赋值操作符
这三个放一起好了,应该也没太多要说的。
前面提过,如果一个方法有一个隐式的参数和一个显式的参数,那么这个方法调用的.和( )都可以省略。省略之后,就像是中置操作符了。什么叫中置操作符这种问题,就自己做功课去吧。所以,中置操作符就是个方法,定义操作符也就是定义个方法这么简单。
一元操作符只有一个参数。如果出现在参数之后,就是后置(postfix)操作符;出现在参数之前,就是前置(prefix)了。四个操作符,+ – ! ~,可以作为前置操作符,它们将会被转换城名为 unary_<prefix>的方法,是四个操作符之一。如-a 相当于 a.unary_-。
赋值操作符,就是常见的 +=、 -=这种感觉了。
赋值操作符有些点需要注意:

<=、>=、!=不是赋值操作符;
以=号开头的操作符不是赋值操作符;

优先级
在学习C和Java的时候,这个话题应该是很早就提出来的。但Scala中却放到了这么后面。理由呢?因为Scala可以自己随意定义操作符,所以首先需要了解其他的东西,等能够自己定义操作符了,再来学习优先级问题。
除了赋值操作符之外,优先级由操作符的首字符决定。



最高优先级:除以下字符外的操作符字符


* / %


+ -


:


< >


! =


&


^


|


非操作符


最低优先级:赋值操作符



需要注意的再强调一下,优先级是由操作符的首字符决定的;赋值操作符的优先级是最低的。后置操作符的优先级低于中置操作符。
优先级这个东西,随着使用经验的增长,加上括号的使用,不会成为大问题才是。
结合性
结合性决定了当有一系列同一优先级的操作符时,它们的求值顺序。在Scala中,绝大部分的操作符都是左结合(从左到右)的,除了:1. 以冒号:结尾的操作符;2. 赋值操作符。而且,在右结合时,二元操作符的参数顺序也发生了变化:二元操作符是其第二个参数的方法。
// ::操作符用来构造列表
1 :: 2 :: Nil // Nil表示空列表
// 上面表达式表示:
1 :: (2 :: Nil)
// 如果变为完整的方法调用:
(Nil.::(2)).::(1) // 注意右结合时参数顺序的变化
apply和update方法
如果f不是方法或者是函数:
f(arg1, arg2, ...)
// 相当于
f.apply(arg1, arg2, ...)

f(arg1, arg2, ...) = value
// 相当于
f.update(arg1, arg2, ..., value)
apply方法经常被用在伴生对象中用来构造对象,可以省去使用new来创建对象。而update方法经常被使用在于数据和映射之类的集合有关的地方。
提取器(L2)
提取器是一个带有unapply方法的对象。unapply方法算是apply方法的反向操作:unapply接受一个对象,然后从对象中提取值,提取的值通常是用来构造该对象的值。
// 有一个表示分数的Fraction类以及其伴生对象
class Fraction(n: Int, d: Int) {
  ...
}

object Fraction(n: Int, d: Int) {
  def apply(n: Int, d: Int) = new Fraction(n, d)
  def unapply(input: Fraction) =
    if (input.den == 0) None else Some((input.num, input.den))
}

// 取出值
var Fraction(a, b) = Fraction(3, 4) * Fraction(2, 5)
// 用于模式匹配
case Fraction(a, b) => ...
每一个样例类都自动具备apply和unapply方法,样例类在14章介绍。
带单个参数或无参数的提取器(L2)
在Scala中,没有只带一个组元的元组(这里可推测上面的提取器通常是使用元组来进行的)。如果unapply方法要提取单值,应该返回一个目标类型的Option。
object Number {
  def unapply(input: String): Option[Int] =
  try {
    Some(Integer.parseInt(input.trim)
  } catch {
    case ex: NumberFormatException => None
  }
}
提取器可以只测试其输入而不将值提取出来,此时,unapply方法应返回Boolean。
object IsCompound {
  def unapply(input: String) = input.contains(" ")
}

// 使用(模式匹配的内容还没有学,所以暂时还不能理解)
author march {
  case Name(first, last @ IsCompound()) => ...
  case Name(first, last) => ...
}
unapplySeq方法(L2)
要提取任意长度的值的序列,使用unapplySeq方法。此方法返回Option[Seq[A],A是被提取的值的类型。
object Name {
  def unapplySeq(input: String): Option[Seq[String] =
    if (input == "") None else Some(input.trim.split("\\s+"))
}

posted on 2016-04-13 10:27  阚壠  阅读(601)  评论(0)    收藏  举报