2021-01-15
< view all postsScala不仅是一个函数式语言,也是一个面向对象的语言。实际上Scala在OOP特点方面和Java是保持一致的:万物皆对象,包括基础类型。而Scala在语法上还提供了一个方便的特性,就是可以用纯符号作为函数名(方法名)的关键字,从而使我们可以方便地在Scala去实现自定义的类型,包括各种基础类型本身。
首先来看对布尔类型的实现,这里一个值得注意的地方是取非的符号"!"。其它的逻辑运算符,后面都是跟着一个参数的,比如a.||(b),在Scala中可以写成a || b,这实际上也是一个语法糖了。那么像取非这样后面没有参数的前缀(prefix)运算符,在定义的时候就必须在前面加上"unary_"作为标注,也就是写成def unary_! : Boolean。还有一个注意点,这里冒号的前面必须要有一个空格,否则编译器会以为冒号也是方法名的一部分。
package basetype.simulation abstract class Boolean { def conditional(x: Boolean, y: Boolean): Boolean def &&(that: Boolean): Boolean = conditional(that, _false) def ||(that: Boolean): Boolean = conditional(_true, that) // prefix必须有unary_前缀,且和冒号之间必须有一个空格 def unary_! : Boolean = conditional(_false, _true) def ==(that: Boolean): Boolean = conditional(that, !that) def !=(that: Boolean): Boolean = conditional(!that, that) def >(that: Boolean): Boolean = conditional(!that, _false) def <(that: Boolean): Boolean = conditional(_false, that) def toString: String } object _true extends Boolean { def conditional(x: Boolean, y: Boolean): Boolean = x override def toString: String = "true" } object _false extends Boolean { def conditional(x: Boolean, y: Boolean): Boolean = y override def toString: String = "false" }
来做一些简单的测试:
package basetype.simulation object entry extends App{ println(_true && _false) println(_true || _false) println(_false && _true) println(!_true) println(!_false == _true) println(_false != _true) println(_false > _true) println(_true > _false) }
结果是:
false true false false true true false true
其实实现布尔类型本质上就是做了一个枚举,因为True和False在运算符的两边总共也只有4种排列组合的方式。一个比较巧妙的地方是把conditional()给改成泛型和call by name之后,它实际上可以起到一个模拟if/else的作用:
abstract class Boolean { def conditional[T](x: => T, y: => T): T def toString: String } object _true extends Boolean { def conditional[T](x: => T, y: => T): T = x override def toString: String = "true" } object _false extends Boolean { def conditional[T](x: => T, y: => T): T = y override def toString: String = "false" }
用法是 条件.conditional(为true时的执行内容, 为false时的执行内容),例如:
object entry extends App{ _false conditional(println("is true"), println("is false")) }
首先这里为什么需要改成泛型,因为conditional方法本身不关心它接受和返回的值,只是从两者中选择一个去执行。第二,为什么需要改成CBN,因为Scala中函数的调用默认是CBV的,因此默认情况下,在conditional能做出判断前,两个参数里的内容都会被执行。改成CBN就只会执行一个。实际上上面的与和或运算符也应该改成CBN的调用,提高效率。关于CBN和CBV,这篇笔记有详细的解释。
接下来模拟自然数类型(大于等于0的整数),为简单起见,当数值小于0时会直接抛出异常。首先定义下面的抽象类:
package nat.simulation abstract class Nat { def isZero: Boolean def predecessor: Nat def successor: Nat def + (that: Nat): Nat def - (that: Nat): Nat def toString: String }
给这个抽象类设计两个子类,其中一个是Object 0,另一个是给输入加1的累加器Class。0很好实现,任何数和0相加都是它本身,而0减去非0的数直接抛出异常即可。0递减1也是异常,而加1则可以返回一个累加器对象。
object Zero extends Nat{ override def isZero: Boolean = true override def predecessor: Nat = throw new RuntimeException("negative number") override def successor: Nat = new Succ(Zero) override def +(that: Nat): Nat = that override def -(that: Nat): Nat = if (that.isZero) Zero else throw new RuntimeException("negative number") override def toString: String = "." }
那么接下来就是累加器了(每次加1),这个其实结合数据结构的知识也是容易想到的,尤其是我们已经知道递归函数的执行是转换为调用栈。累加器的作用是输入对象n,返回对象n+1,所以这个累加器对象的递减直接返回n就行。累加器对象的递增则是递归调用自己。加法和减法可以通过代换模型的表达式推导,例如:
3+2 = 4+1 = 5+0
3-2 =2-1 =1-0
将右侧表达式最终转化为0,达到停止条件,直接返回左侧即可:
class Succ(n: Nat) extends Nat { override def isZero: Boolean = false override def predecessor: Nat = n override def successor: Nat = new Succ(this) override def +(that: Nat): Nat = if (that.isZero) this else this.successor + that.predecessor override def -(that: Nat): Nat = if (that.isZero) this else this.predecessor + that.predecessor override def toString: String = "|" + n.toString }
做这样的测试:
object entry extends App{ println(Zero.successor.successor+Zero.successor-Zero.successor) }
输出为"||.",正确。