【问题标题】:scala function - how does it work?scala 函数 - 它是如何工作的?
【发布时间】:2015-02-06 11:55:13
【问题描述】:

我是 scala/java 的新手,我试图理解下面的代码,它返回目录中的文件列表。 Function source

  1. 它接受一个名为 dir 的参数 - dir 的类型是 File 还是 File 对象?

  2. 它返回一个file 类型的数组。

  3. 它在dir 上调用listFiles 方法。

  4. 最后一行是做什么的?

    
    def getRecursiveListOfFiles(dir: File): Array[File] = {
     val these = dir.listFiles
     these ++ these.filter(_.isDirectory).flatMap(getRecursiveListOfFiles)
    }

【问题讨论】:

    标签: scala


    【解决方案1】:

    此代码使用递归进行广度优先搜索。

    File 可以是文件,也可以是目录。

    代码dir.listFiles 列出目录中的所有文件。请记住,这将是文件和目录的列表!

    然后我们可以将最后一行分解为 3 件事。这些很容易成为单独的行。

    1. these.filter(_.isDirectory) 将返回需要搜索的目录列表。它会过滤掉文件。
    2. flatMap(getRecursiveListOfFiles) 获取此目录列表,并为每个目录调用 getRecursiveListOfFiles。然后,它将这些结果展平到一个列表中。
    3. ++ 将两个数组相加。我们将these 添加到递归调用的结果中。

    flatMap 是这里的关键。阅读它,了解它与 map 函数的不同之处,以充分了解发生了什么。

    【讨论】:

    • 这是最清楚的解释。值得补充的是,这种方法可以写成尾递归(@tailrec)。我认为这样做对原始海报来说可能是一个很好的练习。
    【解决方案2】:

    简而言之:

     these ++ these.filter(_.isDirectory).flatMap(getRecursiveListOfFiles)
    

    基本上是:

     val allSubDirectories:Array[Files] = these.filter(_.isDirectory)
     allSubDirectories.flatMap(getRecursiveListOfFiles)
     //i.e. for each sub-directory, again find all files in sub-directory
     these ++ (files of all sub-directories)
     //ultimately add files of sub-directory to the actual list
    

    另一种理解方式是:

    def getAllFiles(dir: File): List[File] = {
     val these = dir.listFiles.toList
     these ::: these.filter(_.isDirectory).map(x => getAllFiles(x)).flatten
    }
    

    基本相同的控制流程,对于每个子目录,您会获得所有文件的列表,然后在同一个列表中添加子目录的文件。

    【讨论】:

      【解决方案3】:

      我将尝试引导您了解我如何看待这个人试图阅读一段未知代码。

      通过上下文应该很清楚File 可以是常规文件或目录。它不包含文件内容,但代表文件系统中的任何条目,您可以使用其他库命令打开文件。对于这个函数的目的,File 只是在它是一个目录的情况下恰好包含更多Files 的东西,并且这些可以通过方法listFiles: List[Files] 访问。据推测,它还提供了其他信息,以便getRecursiveListOfFiles 的原始调用者可以对结果列表做一些事情。

      同样根据上下文,these 很明显是当前目录中的条目。

      最后一行是最微妙的。但为了分解它,它使用在 these 中的那些条目中找到的 Files 来扩充 these,这些条目恰好是目录。

      为了解释这一步,List[File] 上的flatMap 的签名可以被认为是flatMap[B](f: File => List[B]): List[B],其中B 是一个类型变量。在这种情况下,因为相同的函数getRecursiveListOfFiles(类型为File => List[File])被递归传递,所以B 只是File,所以我们可以将这个特定的调用视为flatMap(f: File => List[File]): List[File]

      粗略地说,flatMap 将函数f 应用于容器中的每个项目,其中f 需要返回相同类型的容器。 “扁平”部分只是这些单独的容器组合在一起的事实,而不是嵌套的事实,这就是 map 会做的事情。这就是允许函数递归添加在其子目录中找到的所有文件的原因。很漂亮。

      【讨论】:

        【解决方案4】:
        1. 您要区分什么? dir 的类型是File

        2. Files 的数组,是的。

        3. 它可能隐式地将 these 转换为 scala ListBuffer,这是令人困惑的部分(例如,它可能导入 scala.collection.JavaConversions._ - 最好使用 JavaConverters,这使得转换显式致电.asScalaasJava)。您可以在the scaladoc 中找到++filterflatMap 的定义,希望足以了解发生了什么。

        (注意:由于降价,我的 4. 以 3. 的形式出现:()

        【讨论】:

          猜你喜欢
          • 2012-01-28
          • 1970-01-01
          • 2011-02-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多