Scalaを使ってみる: (2) 英単語の抽出

このエントリーをはてなブックマークに追加

Scalaを勉強している.勉強中の身ではあるが,以下を例題として,Scalaプログラムの作り方について説明してみる.

テキストファイル中に現れる英単語の出現回数を数えて,出現回数の多い語から表示する.
入力のテキストファイルとしては,Project Gutenberg 中のHalmet を用いる(ファイル名を hamlet.txt,改行はLFにした).
なお利用している環境は,Ubuntu 8.04 LTS上の Scala 2.8.0 RC2 (2010年5月10日リリース), Java 1.6.0である.

正規表現 (Regex)

各行の文字列を英単語毎に分解するには,正規表現のクラスscala.util.matching.Regex を用いる.
たとえば「"[a-zA-Z]+".r」が英単語にマッチする正規表現である.

  scala> "[a-zA-Z]+".r
  scala.util.matching.Regex = [a-zA-Z]+

findAllInで,正規表現にマッチする文字列のIteratorが返ってくる.

  scala> "[a-zA-Z]+".r.findAllIn("Hamlet, Prince of Denmark")
  scala.util.matching.Regex.MatchIterator = non-empty iterator

  scala> "[a-zA-Z]+".r.findAllIn("Hamlet, Prince of Denmark").toList
  List[String] = List(Hamlet, Prince, of, Denmark)

なお正規表現として「"\\p{InCJK_UNIFIED_IDEOGRAPHS}+".r」を用いれば,漢字にマッチする文字列を抽出できる.

mapメソッド

mapで,各要素に関数を適用できる.
たとえば,以下のようにするとリスト中の各文字列の文字を小文字に変換できる.

  scala> List("Hamlet", "Prince", "of", "Denmark").map(_.toLowerCase)
  List[String] = List(hamlet, prince, of, denmark)

文字列から英単語を抽出する関数

Regexとmapを用いて,与えられた文字列から英単語をIteratorとして抽出する関数toWordsを定義する.

  scala> def toWords(s: String) = "[a-zA-Z]+".r.findAllIn(s).map(_.toLowerCase)
  toWords: (s: String)Iterator[java.lang.String]

  scala> toWords("Hamlet, Prince of Denmark")
  Iterator[java.lang.String] = non-empty iterator

  scala> toWords("Hamlet, Prince of Denmark").toList
  List[java.lang.String] = List(hamlet, prince, of, denmark)

flatMapメソッド

入力ファイルの各行に,上のtoWordsを適用すれば良いのだが, mapを用いるとIteratorIteratorになる.

  scala> getLines("hamlet.txt").map(toWords)
  Iterator[Iterator[java.lang.String]] = non-empty iterator

Iteratorを統合し,一つのIteratorにまとめるにはflatMapを用いる.

  scala> getLines("hamlet.txt").flatMap(toWords)
  Iterator[java.lang.String] = non-empty iterator

  scala> getLines("hamlet.txt").flatMap(toWords).size
  Int = 34743

  scala> getLines("hamlet.txt").flatMap(toWords).take(5).toList
  List[java.lang.String] = List(project, gutenberg, etext, of, hamlet)