我来自C ++,我正在尝试使用更多“功能”风格,并决定用文件读取递归功能切换出我的文件读取循环(这是正确的术语吗?),这就是我提出了:
def readFileToList(list: List[String]) : List[String] = list match {
case List(head) => list
case head::tail => fromFile("filename").getLines :: list
case _ => list
}
所以当然这不起作用,我不确定为什么。我看到的第一件事是getLines返回一个迭代器。 那么如何以“功能”的方式做到这一点呢?
答案 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
}