《Scala编程》学习笔记(8~10章)

来源:互联网 发布:淘宝企业店铺名字大全 编辑:程序博客网 时间:2024/05/20 07:34

第8章 函数和闭包

Scala Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
package com.chapter8

import scala.io.Source
/**
 *第8章 函数和闭包
 * 
 * Scala提供了许多Java中没有定义函数的方式。
 * 1、除了作为对象成员函数的方法之外,还有内嵌在函数中的函数,函数字面量和函数值。 
 * 2、函数式编程风格的一个重要设计原则是:程序应该被结构成若干小的函数,每块实现
 *    一个定义完备的任务,每块都非常小。这种风格的好处是它让程序员能够把这些模块
 *    的灵活组装成更复杂的事物。每个小块应该充分简化到足以单独理解。可以用本地函数
 *    或者私有方法达到这个目的。
 */


/**
 *8.1 方法 
 *  对象中的函数被称为方法。
 */

//例子:读取文件,打印出超过指定宽度的文本行
object LongLine {
  
  
def processFile(filename: String,  width: Int) {
    val source = Source.fromFile(filename)
    
for(line <- source.getLines) {
      processLine(filename, width, line)
    }
  }
  
  
private def processLine(filename: String,  width: Int, line: String) {
    
if(line.length() > width) {
      println(filename +
" : " +line)
    }
  } 
  
  
def main(args: Array[String]): Unit = {
    processFile(
"D:\\study\\scalaWorkSpace\\Scala\\test.scala"50)
  }
  
}

/**
 * 8.2 本地函数
 * 1、本地函数就是定义在函数内部的函数。
 * 2、本地函数可以访问外部函数的参数
 */

//改写上一个例子
object LongLine2 {
  
  
def processFile(filename: String,  width: Int) {
    val source = Source.fromFile(filename)
    
for(line <- source.getLines) {
      processLine(line)
    }
    
    
def processLine(line: String) {
      
if(line.length() > width) {
        println(filename +
" : " +line)
      }
    } 
  }

  
def main(args: Array[String]): Unit = {
    processFile(
"D:\\study\\scalaWorkSpace\\Scala\\test.scala"50)
  }
  
}

object Chapter8 {
/**
 * 8.3 头等函数
 * 1、什么是头等函数?
 * 在计算机科学的一个编程语言中,如果它把方法作为一等公民的时候,
 * 那么这个编程语言就含有头等函数(first-class functions)。
 * 特别是,这意味着那个语言支持将函数当作参数传递给其他的函数,
 * 从那其他的函数里返回出值,并且可以将他们设定为变量,或者将他们存储在数据结构中。
 * Scala的函数是头等函数。你不仅可以定义和调用函数,还可以把它们写成匿名的函数字面量,
 * 并把它们作为值传递。函数字面量被编译进类,并在运行期间实例化为函数值。
 * 函数字面量和值的区别在于:函数字面量存在于源代码,而函数值作为对象存在于运行期。
 * 这个区别很像类(源代码)和对象(运行期)之间的关系。
 * 
 */

  
// (x: Int) => x + 1 这是一个函数字面量,=> 指明这个函数把左边部分转化成右边部分. 并将函数字面量存为变量
  def fun1() {
    var increase = (x: Int) => x + 
1
    println(
"increase:  " + increase(10))
  }
  
  
// 如果想让函数字面量包含多条语句,用{},函数的返回值是最后一行表达式产生的值 
  def fun2() {
    var increase = (x: Int) => {
      x + 
1
      println(
"这是increase2")
      x + 
2 
    }
    println(
"increase:  " + increase(10))
  }
  
  
//所有的集合类都有foreach方法。它以函数作为入参,并对每个元素调用该函数
  def fun3() {
    val someNumber = List(-
10,1, -35, -712)
    someNumber.foreach((x: Int) => println(x))
  }
  
  
//集合类型还有filter方法
  def fun4(): List[Int] = {
    val someNumber = List(-
10,1, -35, -712)
    someNumber.filter((x: Int) => x > 
0)
  }
  
  
/**
   * 8.4 函数字面量的短格式
   * Scala 提供了许多方法去除冗余信息并把函数字面量写得更简短。
   * 一种让函数字面量更简短的方式是去除参数类型。
   */

  
def fun6() {
    val someNumber = List(-
10,1, -35, -712)
    
//去除了参数类型
    someNumber.foreach(x => println(x))
  }
  
  
//集合类型还有filter方法
  def fun7(): List[Int] = {
    val someNumber = List(-
10,1, -35, -712)
    
//去除了参数类型
    someNumber.filter(x => x > 0)
  }
  
  
/**
   * 8.5 占位符语法
   * 如果想让函数字面量更加简洁,可以把下划线当作一个或更多参数的占位符。
   * 可以把下划线看作表达式里需要被填补的“空白”,这个“空白”在每次函数被调用的时候用函数的参数填入
   */

  
def fun8() {
    val someNumber = List(-
10,1, -35, -712)    
    
//下面那句与someNumber.filter(x => x > 0)等价
    println(someNumber.filter(_ > 0))
  }
  
  
def fun9() {
    val f = (_: Int) + (_: Int)
    
// 打印出15
    println(f(5,10))
  }
  
  
/**
   * 8.6 部分应用函数
   */

  
def fun10() {
    val someNumber = List(-
10,1, -35, -712)
    
//以下四句的意思是一样的
    someNumber.foreach(x => println(x))
    someNumber.foreach(println(_))
    someNumber.foreach(println _)
    someNumber.foreach(println)
  }
  
  
//偏函数
  def fun11() {
    
def sum(a: Int, b: Int, c: Int) = a + b + c
    
//实例化一个带3个缺失整数参数的函数值,并把这个新的函数值的索引赋给变量a
    val a = sum _ 
    println(a(
123))
    val b = sum(
1, _: Int, 3)
    println(b(
20))
  }
  
  
/**
   * 8.7 闭包
   * 依照函数字面量在运行时创建的函数值(对象)被称为闭包。
   * 不带自由变量的函数字面量,如(x: Int) => x + 1, 被称为封闭项
   * 任何带有自由变量的函数字面量,如如(x: Int) => x + more, 被称为开放项
   * 
   */

  
def fun12() {
    println(
"000000")
    var more = 
1
    val addMore = (x: Int) => x + more 
//more是什么
    //打印出: 11
    println(addMore(10))
    more = 
2
    
//打印出: 12
    println(addMore(10))
  }
  
  
def fun13() {
    var sum = 
0
    val someNumber = List(-
3, -2, -1125)
    someNumber.foreach { sum += _ }
    
//打印出: 2
    println(sum)
  }
  
  
def fun14() {
    
def makeIncreaser(more: Int) = (x: Int) => x + more
    val inc1 = makeIncreaser(
1)
    
//打印出:2
    println(inc1(1))
    
    val inc999 = makeIncreaser(
999)
    
//打印出:1000
    println(inc999(1))
  }
  
  
/**
   *8.8 重复参数
   * 在Scala中,可以指明函数的最后一个参数是重复的,从而允许客户端传入可变长度参数列表。
   * 想要标注一个重复参数,可在参数的类型后面放一个星号。
   */

  
def fun15() {
    
def echo(args: String*) = {
      
for(arg <- args) print(arg + " ")
      println()
    }
    echo(
"one")
    echo(
"one""two")
    echo(
"one""two""three")
    
//上面echo中args的类型的实际上是Array[String]。但是直接传递一个数组会报错!
    val arr = Array("one""two""three")
    
//echo(arry) // 编译不通过
    //在arr后面加上: _* 就可以将数组传入可变长度参数列表。
    //这个标注告诉编译器把arr的每个元素当作参数。
    echo(arr: _*)
  }
  
  
/**
   * 8.9 尾递归
   * 1、在函数最后一个动作调用自己的函数,被称为尾递归
   * 2、Scala检测到尾递归就用新值更新函数参数,然后把它替换成一个回到
   *    函数开头的跳转。这样它的效率就与While循环一样了。
   * 3、尾调优化限定了方法或嵌套函数必须在最后一个操作调用本身,
   *    而不是转到某个函数值或什么其他的中间函数的情况。所以Scala
   *    里,尾递归的使用局限还是很大的。
   */

  
//尾递归的一个例子
   private val theNumber = 999
   
   
private def isGoodEnough(guess: Int) = {
     
if(guess == theNumber)
       true
      
else
       false
  }
    
  
private def improve(guess: Int) = {
    
if(guess > theNumber)
      guess - 
1
    
else 
      guess + 
1
  }
  
  
  
def approximate(guess: Int): Int = {
    println(
"调用一次approximate,guess是:"+guess)
    
if(isGoodEnough(guess)) guess
    
else approximate(improve(guess))
  }
  
  
//尾递归函数追综
  //这个方法不是尾递归,因为最后还做了一步加1的操作
  def boom(x: Int):Int = {
    
if(x == 0throw new RuntimeException
    
else boom(x-1) + 1
  }
  
  
//这个方法是尾递归
  def bang(x: Int):Int = {
    
if(x == 0throw new RuntimeException
    
else bang(x-1)
  }
  
  
//尾递归的局限
  //1、互相递归的函数不能优化
  def isEvent(x: Int): Boolean = 
    
if(x==0) true else isOdd(x-1)
    
  
def isOdd(x: Int): Boolean = 
    
if(x==0) true else isEvent(x-1)
    
  
//2、以下函数也不能优化
  var funValue = nestedFun _
  
  
def nestedFun(x: Int) {
    
if(x != 0) {
      println(x)
      funValue(x-
1)
    }
  }
    
  
//入口程序
  def main(args: Array[String]): Unit = {
    bang(
10)
  }
}


第9章 控制抽象

Scala Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
package com.chapter9

import java.io.File
import java.io.PrintWriter
/**
 * 第9章 控制抽象
 * 高阶函数不仅能帮助减少代码重复,还能使客户代码更简洁
 */


/**
 * 9.1 减少代码重复
 */

//未优化的代码
object FileMatcher {

  
private def filesHere = (new java.io.File(".")).listFiles

  
def filesEnding(query: String) =
    
for (file <- filesHere; if file.getName.endsWith(query))
      yield file

  
def filesContaining(query: String) =
    
for (file <- filesHere; if file.getName.contains(query))
      yield file

  
def filesRegex(query: String) =
    
for (file <- filesHere; if file.getName.matches(query))
      yield file

}

//减少代码重复,根据上面代码进行优化
object FileMatcher2 {

  
private def filesHere = (new java.io.File(".")).listFiles

  
def filesMatching(querying: String, matcher: (StringString) => Boolean) = {
    
for (file <- filesHere; if matcher(file.getName, querying))
      yield file
  }

  
//filesEnding 和 filesEnding2 表达的意思是一样的
  def filesEnding(query: String) =
    
//这边 _是绑定变量,没有自由变量,所以没有采用闭包
    filesMatching(query, _.endsWith(_))

  
def filesEnding2(query: String) =
    filesMatching(query, (fileName: 
String, query: String) => fileName.endsWith(query))

  
def filesContaining(query: String) =
    filesMatching(query, _ contains _)

  
def filesRegex(query: String) =
    filesMatching(query, _ matches _)

}

//对上面代码进一步优化
object FileMatcher3 {

  
private def filesHere = (new java.io.File(".")).listFiles

  
def filesMatching(matcher: (String) => Boolean) = {
    
for (file <- filesHere; if matcher(file.getName))
      yield file
  }

  
//filesEnding 和 filesEnding2 表达的意思是一样的
  def filesEnding(query: String) =
    
//这边 _是绑定变量,query是自由变量,采用了闭包
    filesMatching(_.endsWith(query))

  
def filesContaining(query: String) =
    filesMatching(_.contains(query))

  
def filesRegex(query: String) =
    filesMatching(_.matches(query))

}

object Chapter9 {
  
/**
   * 9.2 简化客户代码
   */

  
//判断传入的值中是否有小于0的
  //未使用高阶函数
  def containsNeg(nums: List[Int]): Boolean = {
    var exists = false
    
for (num <- nums)
      
if (num < 0)
        exists = true
    exists
  }
  
  
//使用高阶函数exits
  def containsNeg2(nums: List[Int]): Boolean = nums.exists(_ < 0)
  
  
//判断List中是否含有奇数
  def containsOdd(nums: List[Int]) = nums.exists { _ % 2 == 1 }
  
  
/**
   * 9.3 柯里化
   * 
   */

  
def fun1() {
    
//未被柯里化的函数
    def plainOldSum(x: Int, y: Int) = x + y
  
    
//柯里化的函数
    def curriedSum(x: Int)(y: Int) = x + y 
    
    println(plainOldSum(
12))
    
//实际上接连调用了两个传统函数。第一个函数调用带单个的名为x的Int参数,
    //并返回第二个函数的函数值。第二个函数带Int参数y。
    println(curriedSum(1)(2))
  }
  
  
/**
   * 9.4 编写新的控制结构
   * 1、在拥有头等函数的语言中,即使语言的语法是固定的,你也可以有效地制作新的控制结构。
   * 所有你需要做的就是创建带函数做参数的方法。
   * 2、任何时候,当你发现你的代码中多个地方有重复的控制模式时,就应该考虑把它实现为一个
   * 新的控制结构。
   * 3、Scala的任何方法调用,如果确定只传入一个参数,就能可选地使用花括号替代小括号包围参数
   */

 
  
def fun2() {
    
def twice(op1: Double => Double, x: Double) = op1(op1(x))
    println(twice(_ + 
1,5))
  }
  
  
//打开一个资源,对它进行操作,然后关闭资源。可以用如下方法将其捕获并放入控制抽象
  def withPrintWriter(file: File, op: PrintWriter => Unit) {
    val writer = 
new PrintWriter(file)
    
try {
      
//把writer借给函数op,这是“借贷模式”
      op(writer)
    } 
finally {
      writer.close()
    }
  }
  
  
//使用上面定义的方法
  def fun3() {
    val file =  
new File("D:\\study\\scalaWorkSpace\\Scala\\test.scala")  
    withPrintWriter (file, writer => writer.println(
new java.util.Date));    
  }

  
  
//对上面的withPrintWriter进行柯里化
  def withPrintWriter2(file: File)(op: PrintWriter => Unit) {
    val writer = 
new PrintWriter(file)
    
try {
      
//把writer借给函数op,这是“借贷模式”
      op(writer)
    } 
finally {
      writer.close()
    }
  }
  
  
def fun4() = {
    val file = 
new File("D:\\study\\scalaWorkSpace\\Scala\\test.scala")
    
//采用柯里化就可以在多个参数的时候使用花括号了,这样的写法更加赏心悦目。
    withPrintWriter2(file) {
      writer => writer.println(
new java.util.Date)
    } 
  }
  
  
/**
   * 9.5 传名参数
   */

  var assertionsEnabled = true
  
  
def byNameAssert(predicate: => Boolean) =
    
if(assertionsEnabled && !predicate)
      
throw new AssertionError
  
  
def fun5() {
    byNameAssert(
5 > 4)    
  }
  
  
def main(args: Array[String]): Unit = {
    fun5()
  }
  
}


第10章 组合与继承

Scala Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package com.chapter10

/**
 * 10.2 抽象类
 * 1、抽象类由abstract修饰符修饰
 * 2、Scala的抽象方法的声明中不需要也不允许有abstract修饰符
 * 
 * 10.3 定义无参方法
 * 1、def height(): Int 称为空括号方法; def height: Int 称为无参方法。
 *    推荐的用法是:无论何时,只要方法中没有参数并且方法仅能通过读取所包含对象的
 *    属性去访问可变状态(特指方法不能改变可变状态),就使用无参方法。
 * 2、下面用无参方法和属性定义height和width从客户的观点看是完全相同的。
 *    唯一的差别是访问字段比调用方法略快,因为字段值在初始化时被预计算。
 *    而方法调用在每次调用时都要计算。特别地,如果类的字段编程了访问方法,
 *    只要访问方法是纯的,即不产生副作用也不依赖可变状态,那么客户代码就
 *    不应该关心是用哪种方式实现的。
 * 3、Scala在遇到混合了无参数和空括号方法的情况时很自由。特别地,你可以用
 *    空括号方法重写无参数方法,反之亦然。
 * 4、Scala在函数调用时可以省略所有的空括号。但是以下情况建议加上空括号:
 *    例如,如果方法执行了I/O,或写入可重新赋值的变量(var),或读取不是调用者
 *    字段的var,总之只要使用了可变对象,都应添加空括号。
 * 5、Scala鼓励将不带参数,并且没有副作用的方法定义为无参数方法的风格。即省略空括号。
 *    但是永远不要定义没有空括号的带副作用的方法。
 */

//abstract修饰符说明类可能有未实现的方法,所以不能被实例化
abstract class Element {
  
//contents被声明为没有实现的方法。换句话说,这个方法是Element类的抽象成员。
  //Scala的抽象方法的声明中不需要也不允许有abstract修饰符
  def contents:Array[String]
  
  
//注意: def height(): Int 称为空括号方法; def height: Int 称为无参方法。
  def height: Int = contents.length
  
//height 也可以由属性来实现,只要将def改成val即可
  //val height: Int = contents.length
  //width 方法返回第一行的长度
  def width: Int = if(height == 00 else contents(0).length
  
//height 也可以由属性来实现,只要将def改成val即可
  //val width: Int = if(height == 0) 0 else contents(0).length
  
    
  
// ++ 操作符连接两个数组
  def above(that: Element): Element = {
    
new ArrayElement(this.contents ++ that.contents)
  }
  
  
def beside(that: Element): Element = {
    
new ArrayElement(
        
for(
            
//zip操作符转换成一个二元对数组(称为Tuple2)
            //Array(1, 2, 3) zip Array("a", "b") 将生成Array((1,"a")(2,"b"))
            //如果两个数组中的一个比另一个长,zip将舍弃多余元素,上面舍弃了3
            (line1, line2) <- this.contents zip that.contents
        )yield line1 + line2
    )
  }
  
  
//注意:toString没有带空参数列表。这遵循了统一访问原则的建议,因为toString是一个不带任何参数的纯方法
  override def toString = contents mkString "\n"
  
}

package com.chapter10
/**
 * 10.4 扩展类
 * 1、使用extends进行扩展
 * 2、extends使得子类继承超类的所有非私有成员
 * 3、如果省略extends,Scala中的类默认继承自scala.AnyRef,
 *    相当于Java中的java.lang.Object
 * 
 * 10.5 重写方法和字段
 * 1、Scala里的字段和方法属于相同命名空间。这让字段可以重新无参方法。
 * 2、Scala里禁止同一个类中用同样的名称定义字段和方法。尽管Java允许这么做。
 * 3、Java有四个命名空间:字段、方法、类型和包;
 *    Scala两个命名空间:值(字段、方法、包、单例对象)和 类型(类和特质名)
 *    
 * 10.10 定义final变量
 *    如果想确保一个成员不被子类重写,可以通过添加final修饰符来实现;
 *    如果想该类没有子类,只要在class前加上final即可。
 */

class ArrayElement(cons: Array[String]) extends Element {
  
def contents:Array[String] = cons;
  
  
//用字段重写无参方法
  //val contents:Array[String] = cons;
  
  
//添加final后子类不能重写该方法
  final def demo() {
    println(
"ArrayElement")
  }

}

package com.chapter10

/**
 * 10.6 定义参数化字段
 * 
 */

//同时定义同名的参数和字段的一种简写方式
class ArrayElements2(val contents:Array[String]) extends Element {

}

//再来一个例子
class Cat {
  val dangerous = false
}

//Tiger和Tiger2的意思是一样的,Tiger类中没有其他内容,省略了{}
class Tiger(override val dangerous: Boolean, private var age: Int) extends Cat

class Tiger2(p1:Boolean, p2:Int) extends Cat {
  override val dangerous = p1;
  
private var age = p2;
}

package com.chapter10
/**
 * 10.7 调用超类构造器
 * 10.8 使用override修饰符
 *      Scala要求:若子类成员所有重写了父类的具体成员必须带有override修饰符;
 *      若成员实现的是同名的抽象成员时,则这个修饰符是可选的。若成员并未实现或
 *      重写基类里的成员,则禁用这个操作符。
 * 10.9 多态和绑定
 */

class LineElement(s: Stringextends ArrayElement(Array(s)){
  override val width = s.length
  override val height = 
1
    
/*override def demo() { //超类该方法已被final修饰,编译不过
    println("ArrayElement")
  }*/

}

package com.chapter10
/**
 * 10.11 组合和继承
 */

class LineElement2(s: Stringextends Element {
  
//LineElements2 与 Array是组合关系,与Element是继承关系
  val contents = Array(s)
  override val width = s.length
  override val height = 
1
}

package com.chapter10.factory

/**
 * 10.13 定义工厂对象
 * 1、工厂对象包含了构建其他对象的方法。客户可以使用这些工厂
 *   方法构造对象,而不是直接使用new构造对象。
 * 2、工厂对象的好处有:
 *   1)将对象的创建集中化并且隐藏对象实际代表的类的细节。
 *   2)可以让客户更容易理解你的库因为暴露的细节更少。
 *   3)给你更多的机会在不破还客户代码的前提下改变库的实现。
 *
 * 3、为构建工厂方法的首要任务是选择工厂方法应该放在何处。
 *   一个直接的方法是创建Element类的伴生对象并把它作为布局元素的工厂方法。
 *   使用这种方式,你要唯一暴露给客户的是Element的类/对象组合,隐藏子类
 *   ArrayElement和LineElement
 */


abstract class Element {

  
def contents:Array[String]

  
def height: Int = contents.length

  
def width: Int = if(height == 00 else contents(0).length

  
def above(that: Element): Element = {
    Element.elem(
this.contents ++ that.contents)
  }
  
  
def beside(that: Element): Element = {
    Element.elem(
        
for(
            (line1, line2) <- 
this.contents zip that.contents
        )yield line1 + line2
    )
  }
  
  override 
def toString = contents mkString "\n"
}

object Element {
  
//用私有类隐藏实现
  private class LineElement(s: Stringextends Element {
    val contents = Array(s)
    override val width = s.length
    override val height = 
1
  }
  
  
private class ArrayElement(cons: Array[String]) extends Element {
    
def contents:Array[String] = cons;
  }
  
  
def elem(contents: Array[String]): Element = 
    
new ArrayElement(contents)
  
  
def elem(line: String): Element =
    
new LineElement(line)
}


0 0