如何实现Scala的above,beside和toString

接下来一步,我们将在类Element中实现方法above。把一个元素放在另一个上面是指串连这两个元素的contents值。因此方法above的第一个草案看上去可能是这样的:

def above(that: Element): Element =  



 new ArrayElement(this.contents ++ that.contents)  
++操作符串连两个数组。Scala里的数组被表示为Java数组,但是支持更多的方法。特别是,Scala里的数组继承自类scala.Seq,能够表现象序列这样的结构并包含许多访问和转换序列的方法。本章会解释某些数组方法,更全面的讨论将在第17章。

51CTO编辑推荐:Scala编程语言专题

实际上,前面展示的代码并不完全足够,因为它不允许你把不同长度的元素堆叠在一起。然而本节为了让事情保持简单,我们会任由其状态并仅仅把相同长度的元素传递给above。10.14节里,我们会给above做个改良,这样客户就能用它组合不同长度的元素了。

下一个要实现的方法是beside。把两个元素靠在一起,我们将创造一个新的元素,其中的每一行都来自于两个元素的相应行的串连。如前所述,为了保持事情简单我们会一开始假设两个元素高度相同。这产生了方法beside的下列设计:

def beside(that: Element): Element = {  



 val contents = new Array[String](this.contents.length)  




 for (i < - 0 until this.contents.length)  




  contents(i) = this.contents(i) + that.contents(i)  




 new ArrayElement(contents)  



}  
beside方法首先分配了一个新数组,contents,并串连this.contents和that.contents中相应的数组元素来填充。最终产生了新的ArrayElement包含了新的contents。

尽管beside的这个实现可以工作,但它是指令式风格,马脚露在我们索引数组的循环。这个方法可以替代缩减成一个表达式:

new ArrayElement(  



 for (  




  (line1, line2) < - this.contents zip that.contents  



) yield line1 + line2  


)  
这里,this.contents和that.contents两个数组被使用zip操作符转换为一个对子的数组(可以称为Tupele2)。zip方法从它的两个参数中拣出相应的元素并组织成对子数组。

例如,表达式:

Array(1, 2, 3) zip Array("a", "b")  
将生成:
Array((1, "a"), (2, "b"))  
如果两个操作数组的其中一个比另一个长,zip将舍弃余下的元素。在上面的表达式中,左操作数的第三个元素,3,没有组成结果的部分,因为它在右操作数中没有相对的元素。

结果数组然后通过for表达式被枚举遍历。这里,表达式“for ((line1, line2) < - ...”允许你在一个模式:pattern中命名对子的两个元素,也就是说,line1现在代表对子的第一个元素,line2代表第二个。Scala的模式匹配系统将在第15章描述。现在,你可以就把这当作在每次枚举中定义两个val,line1和line2的方式。

for表达式有个yield部分能产生结果。结果与枚举遍历的表达式类型一致,也就是说,是数组。数组的每个元素都是相应行,line1和line2串连的结果。因此这段代码的最终结果与前一个版本的beside一样,不过因为它避免了显示的数组索引,结果用一种更少犯错的方式实现了。

你还需要一个显示元素的方式。通常,可以通过定义toString方法返回元素格式化成的字串做到。下面是它的定义:

override def toString = contents mkString "\n" 
toString的实现使用了mkString,它被定义在所有序列中,包括数组。正如你在7.8节中看到的,像“arr mkString sep”这样的表达式能返回数组arr所有元素组成的字串。通过调用toString方法每个元素被映射为字串。分隔符字串seq被插入到连续的元素字串当中。因此表达式“contents mkstring "\n"”格式化contents数组为字串,其中每个数组元素占据一行。

请注意toString没有带空参数列表。这个遵循了统一访问原则的建议,因为toString是一个纯的不带任何参数的方法。

附加了这三个方法,类Element现在看上去如代码10.9所展示的。

abstract class Element {  


 def contents: Array[String]  


 def width: Int =  



  if (height == 0) 0 else contents(0).length  



 def height: Int = contents.length  


 def above(that: Element): Element =  



  new ArrayElement(this.contents ++ that.contents)  



 def beside(that: Element): Element =  



  new ArrayElement(  




   for (  




    (line1, line2) < - this.contents zip that.contents  



   ) yield line1 + line2  


  )  



 override def toString = contents mkString "\n" 



}  

相关推荐