Scala:逐行读取文件以递归列出而不是循环

时间:2015-06-28 14:05:13

标签: scala recursion functional-programming

我来自C ++,我正在尝试使用更多“功能”风格,并决定用文件读取递归功能切换出我的文件读取循环(这是正确的术语吗?),这就是我提出了:

 def readFileToList(list: List[String]) : List[String] = list match {
    case List(head) => list
    case head::tail => fromFile("filename").getLines :: list
    case _ => list
  }

所以当然这不起作用,我不确定为什么。我看到的第一件事是getLines返回一个迭代器。 那么如何以“功能”的方式做到这一点呢?

2 个答案:

答案 0 :(得分:2)

我认为你过于复杂:getLines确实会返回一个迭代器,正如你所说的那样。您可以使用.toList方法将迭代器实现到列表中:

Source.fromFile("myfile.txt").getLines.toList

顺便说一句,如果.toList方法不存在怎么办?然后,您可以使用递归函数将迭代器转换为列表(如果您真的想要 - 简单的简化将更简单,可能更快):

def materialize[T](it:Iterator[T]):List[T] = 
    materializeHelper(it, List.empty[T])

def materializeHelper[T](it:Iterator[T], accumulator:List[T]):List[T] =
    if(!it.hasNext) {
        accumulator.reverse  
    }
    else {
        materializeHelper(it, it.next :: accumulator)
    }

通常,您应该尽可能使用高阶函数进行递归或迭代。高阶函数可以最大限度地减少您对移动部件的暴露,这是引入错误的地方。考虑使用foldLeft实现的相同代码:

def materialize[T](it:Iterator[T]):List[T] =
  it.foldLeft(List.empty[T]) { 
    (accumulator, elem) => elem :: accumulator 
  }.reverse

这不容易出错:您不必直接处理迭代器,或者在迭代器为空时处理边界条件。

答案 1 :(得分:1)

所以,你的解决方案是一种奇怪的混合体。函数式编程并不意味着添加某种形式的样板或避免重用。如果您打算使用函数Source.fromFile(...),它可以在内部执行递归函数,那么就像Pascal Bugnion建议的那样使用它。如果你想作为一种练习,要理解内部迭代"内部迭代"可能在函数上使用递归而不是循环实现,然后跳过fromFile(...)方法并自行实现。

这可能与Pascal Bugnion的materialize实施非常相似:

import java.io.{BufferedReader, FileReader, File}
import scala.annotation.tailrec

def linesFromFile( file : File ) : List[String] = {

   // stealing from Pascal Bugnion, slightly different style
   def materialize( br : BufferedReader ) : List[String] = materializeReverse( br, Nil ).reverse

   @tailrec
   def materializeReverse( br : BufferedReader, accumulator : List[String] ) : List[String] = {
     br.readLine match {
       case null => accumulator
       case line => materializeReverse( br, line :: accumulator )
     }
   }

   // presuming the default character encoding is good enough
   val br = new BufferedReader( new FileReader( file ) )
   try materialize( br ) finally br.close
}