読者です 読者をやめる 読者になる 読者になる

関数型プログラミングっぽいファイル検索 - Listモナド

Java Scala

関数脳の習得に向け、Scala関数型プログラミングっぽいファイル検索を書いてみる事にしました。

テーマは List モナド
個人的な理解では、List モナドはリストの内容を変化(増減や値の変更)させる処理を繋げていくようなイメージです。

使用した環境は以下。
以下のサンプルは Scala 2.7 では動作しないのでご注意ください。

それでは、List モナドを活用してみたファイル検索のサンプルです。(処理効率などは考慮していないので注意)

実行時に引数で指定したディレクトリ以下の全ての HTML ファイル(拡張子が .html)を List 化し、個々のファイルの内容から href="xxx" の箇所を全て取り出し、Link に変換しています。

search.scala
import java.io.File
import scala.io.Source

//File を Source に型変換(f.mkString の箇所で適用される)
implicit def toSource(f: File): Source = {
    Source.fromFile(f)("UTF-8")
}

//指定拡張子の全ファイルを取得(サブフォルダも含む)
def listAllFiles(extension: String)(f: File): List[File] = {
    if (f.isDirectory()) {
        f.listFiles().toList.flatMap(listAllFiles(extension))
    }
    else {
        List(f).filter(_.getName().endsWith(extension))
    }
}

//正規表現
val Href = """href="([^"]+)"""".r

case class Link(href: String, file: File)

//指定ディレクトリ(args(0))以下の HTML ファイルの中から
//href="xxx" の箇所を全て抜き出し Link に変換
val links = listAllFiles(".html")(new File(args(0))).flatMap {f =>
    Href.findAllIn(f.mkString).toList.map {s =>
        val Href(href) = s
        Link(href, f)
    }
}

//結果出力
println("total: " + links.length)
links.foreach {link =>
    println(link.href + ", " + link.file.getName())
}

実行結果は以下の通り。

実行例
>scala search.scala jaxrs-1.0-pr-javadoc\index-files
total: 26
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1, index-1.html
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2, index-1.html
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3, index-1.html
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4, index-1.html
・・・