Scala 编程风格练习

Exercises in programming style with Scala

我最近开始阅读“Exercises in programming style”这本书,其中一项任务是用您选择的语言实现每种编程风格。我决定使用 Scala(我对它还很陌生)并且我已经坚持使用第一种 "good old school" 风格。约束是:

  • Very small amount of primary memory, typically orders of magnitude smaller than the data that needs to be processed/generated. (The example sets the limit to 1024 cells)

  • No labels -- i.e. no variable names or tagged memory addresses. All we have is memory that is addressable with numbers.

原始示例(逐行读取文件并计算字数)在 Python 中,如下所示:

data = []
data.append([])    # data[1] is line (max 80 characters)
data.append(None)  # data[2] is index of the start_char of word
data.append(0)     # data[3] is index on characters, i = 0
data.append(False) # data[4] is flag indicating if word was found
data.append('')    # data[5] is the word
data.append('')    # data[6] is word,NNNN
data.append(0)     # data[7] is frequency
...

f = open(sys.argv[1])
# Loop over input file's lines
while True:
    data[1] = [f.readline()]
...

所以我们看到有一些变量(f 和数据),但主要思想是将其保持在最低限度并使用 python 数组作为一堆 "memory addresses".

甚至可以在 Scala 中实现老式编程风格(没有变量名或标记内存地址)吗?具体有没有办法在读取文件内容时避免"line"变量?

for (line <- Source.fromFile("example.txt").getLines) {
  println(line.toUpperCase)
}

将文件内容读入类似于原始示例的数组不起作用,因为它没有提取器(值数据不是大小写class,也不是有一个 unapply/unapplySeq 成员).

P.S。我非常清楚整个任务在 Scala 中可能是 5 行,但这不是重点。

要获取给定文件中的总字数,可以使用以下 Scala 代码:

Source.fromFile("example.txt")
      .getLines.map { line => line.trim.split(" ").length}
      .reduceLeft { _ + _ }

当然你可以避免在 data-array 之外引入变量(并以命令式的方式解决问题)。只需将所有内容放入数组中,而不是将其分配给局部变量。

显然,代码将是一场噩梦,因为数组不会被键入并且您不会为任何数据命名任何有意义的名称,但我假设这就是您的目标这个练习。

import scala.io.Source

/**
 * data 0 : file as line iterator
 * data 1 : index of first unused data cell
 * data 2 : current line
 * data 3 : index of the first letter of the current word
 * data 4 : index of the last letter of the current word
 * data 5 : current word
 * data 6 : temp index to find already initialized words
 * data 7 : flag: Word found
 * data 8, 10, 12, ... words
 * data 9, 11, 13, ... frequencies
 */
object GoodOldSchool {
  def main(args: Array[String]): Unit = {
    val data: Array[Any] = new Array[Any](1024)
    data(0) = Source.fromFile(args(0)).getLines()
    data(1) = 8 // first free cell
    while (data(0).asInstanceOf[Iterator[String]].hasNext) {
      data(2) = data(0).asInstanceOf[Iterator[String]].next()
      data(3) = 0 // index first letter of current word
      data(4) = 0 // index last letter of current word
      // find index last letter of current word
      while (data(4).asInstanceOf[Int] < data(2).asInstanceOf[String].length) {
        // find the next space (we ignore punctuation)
        while (data(4).asInstanceOf[Int] < data(2).asInstanceOf[String].length && data(2).asInstanceOf[String].charAt(data(4).asInstanceOf[Int]) != ' ') {
          data(4) = data(4).asInstanceOf[Int] + 1
        }
        data(5) = data(2).asInstanceOf[String].substring(data(3).asInstanceOf[Int], data(4).asInstanceOf[Int]) // current word
        data(6) = 8 // cell index
        data(7) = false // word already found
        8 until data(1).asInstanceOf[Int] by 2 foreach { _ =>
          // Here, we do a case-sensitive word comparison
          if (data(5) == data(data(6).asInstanceOf[Int])) {
            data(data(6).asInstanceOf[Int] + 1) = data(data(6).asInstanceOf[Int] + 1).asInstanceOf[Int] + 1 // increment frequency
            data(7) = true
          }
          data(6) = data(6).asInstanceOf[Int] + 2
        }
        if (data(7) == false) {
          // create new frequency, because word was not discovered before
          data(data(1).asInstanceOf[Int]) = data(5) // set word
          data(data(1).asInstanceOf[Int] + 1) = 1 // set frequency
          data(1) = data(1).asInstanceOf[Int] + 2 // used up two cells, update index of next free cell
        }
        // move to next word
        data(3) = data(4).asInstanceOf[Int] + 1
        data(4) = data(3)
      }
    }

    data foreach println // let's have a look at our result
  }

}