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

Scalaで新しく制御構造を作成する

scala

Scalaで新しい制御構造を自作する

Scala関数型言語にあるため関数が第一級のオブジェクトとなります。そのためJavaなどのオブジェクト指向言語と比べてどんな違いがあるかというと、関数を引数にして別の関数を呼び出すことができます。俗に言う高階関数というやつです。
試しに用意した関数を引数として受け取るサンプルは以下のようになります。

def listFunc(lines: List[String])(op:String => Boolean) = {
  for {
    line <- lines
    if op(line)
  } yield line + "\n"
}

(op:String => Boolean) の部分がStringを引数で受け取りBooleanを受け取る関数を引数で使うように定義している。(op:String => Boolean)の部分は(op: Function[String, Boolean])と書き換えることもできます。それから関数を引数として渡している部分は以下のようになります。

def grepText(fileName: String, text: String): String = {
  val lines = Source.fromFile(fileName).getLines().toList
  val result = listFunc(lines) {
    line => line.contains(text)
  }
  result.mkString
}

高階関数を利用することで処理のみを切り替えるだけということができます。上記の例ではline.contains(text)の部分が異なる関数を利用することで制御の構造はそのままで処理を切り替えることができます。また同一の処理を別の部分でも再利用が行えるためコードの再利用性はjavaなどに比べて大分上がると思います。
参考までに今回用意した高階関数を利用したgrepコマンドの全体像は以下のようになります。

import scala.io.Source

/**
  *
  * @param file ファイル名
  * @param text 検索対象テキスト
  * @param lineNum 行番号を表示するかどうか
  */
case class SGrepArgs(file:String, text: String, lineNum: Boolean )
object SGrep {

  def parseArgs(args: List[String]): Option[SGrepArgs] = {
    var file: String = ""
    var text: String = ""
    var lineNum:Boolean = false

    def parseArgsHelper(args: List[String]): Unit = {
      args match {
        case "-n" :: x  ::rest => {
          lineNum = if(x equals "true"){true} else{false}
          parseArgsHelper(rest)
        }
        case x :: y :: rest => {
          file = x
          text = y
          parseArgsHelper(rest)
        }
        case _ => {}
      }
    }
    parseArgsHelper(args)
    Some(SGrepArgs(file, text, lineNum))
  }

  def main(args:Array[String]): Unit ={
    val sGrepArgs = parseArgs(args.toList)
    sGrepArgs match {
      case Some(x) => grep(x)
      case _ => {}
    }
  }


  def grep(sGrepArgs: SGrepArgs) = {
    println(grepText(sGrepArgs.file, sGrepArgs.text))
  }

  def grepText(fileName: String, text: String): String = {
    val lines = Source.fromFile(fileName).getLines().toList
    val result = listFunc(lines) {
      line => line.contains(text)
    }
    result.mkString
  }

  def listFunc(lines: List[String])(op:String => Boolean) = {
    for {
      line <- lines
      if op(line)
    } yield line + "\n"
  }
}