1

我是 Scala 的新手,所以我只是在尝试。以下代码递归地打印出 D:\Downloads 下所有文件的名称:

import java.io.File

object Run {
  def main (args: Array[String]){
    //read each file
    val f = new File("""D:\Downloads""");
    listFiles(f)
  }

  def listFiles(f: Any): Unit= f match{
    case f:File if f.isDirectory => f.listFiles().deep.foreach(listFiles(_))
    case f:File if f.isFile => println(f.getName)
    case _ => Unit
  }
}

这行得通。现在,我希望 listFiles 建立一个字符串列表并返回它。这就是我所做的:

  def listFiles(f: Any): List[String] = f match{
    case f:File if f.isDirectory => f.listFiles().foreach(listFiles(_))
    case f:File if f.isFile => List(f.getName)
    case _ => Nil
  }

当 f 是目录时,foreach 应该递归调用 listFiles 并返回一个 List。如何将所有这些数组连接在一起并返回它们?有没有更好的方法来做到这一点?

4

4 回答 4

5

以下内容可能会有所改进,但它应该是一个可以接受的起点:

def listFiles(f: File) = {
  def run(f: File, acc: List[File]): List[File] =
    if(f.isFile) f :: acc
    else         f.listFiles.foldLeft(acc) {(l, f) => run(f, l)}

  run(f, Nil)
}
于 2013-09-18T16:49:30.660 回答
5

从您的方法工作,使用flatMap

def listFiles(f: Any): List[String] = f match{
  case f:File if f.isDirectory => f.listFiles().toList.flatMap(listFiles(_))
  case f:File if f.isFile => List(f.getName)
  case _ => Nil
}
于 2013-09-18T16:53:37.063 回答
2

您应该flatmap在文件列表中使用 a ,但是因为我更喜欢 for-comprehensions 的外观,所以在这里:

 def listFiles(f: Any): List[String] = f match {
   case d: File if d.isDirectory => for {
      file <- d.listFiles.toList
      fileName <- listFiles(file)
      } yield fileName

   case f: File if f.isFile => List(f.getName)
   case _ => Nil
 }    

另一方面,您应该寻求更强的类型安全性并将方法定义为:

 def listFiles(f:File): List[String]

这样它只能用File参数调用,你可以在编译时捕获更多错误。

于 2013-09-18T16:59:07.237 回答
1

在尝试各种实现时,我最终对这个问题很感兴趣。他们中的大多数人在我的电脑上花了一秒钟的时间列出了一个相当嵌套的目录,总共大约有 4500 个文件。

我使用 Stream 编写了一个实现,用于您可能一次不需要所有文件的情况。当您强制获取每个文件时,它的性能似乎与 List 实现一样好。

import java.io.File

def listFiles(f : File): Stream[File] = {
  def streamFiles(fs : List[File]): Stream[File] = fs match {
    case x::xs => x match {
      case x if x.isDirectory => streamFiles(x.listFiles.toList) #::: streamFiles(xs)
      case x if x.isFile      => x #:: streamFiles(xs) 
    }
    case Nil    => Stream.empty
  }

  if (f.isDirectory)
    streamFiles(f.listFiles.toList)
  else
    f #:: Stream.empty
}

这将懒惰地产生您的文件列表。

于 2013-09-19T15:55:00.873 回答