SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

今からでも遅くない これから始めるScala

今からでも遅くない これから始めるScala(中編)

関数型言語としてのScala


関数をオブジェクトとして扱う

 では、関数をオブジェクトとして扱えるということが、どのように便利であるかを説明します。

高階関数

 関数をオブジェクトとして扱えるということは、関数の引数に関数オブジェクトを渡したり、関数オブジェクトを新しく生成して返す関数を定義できます。このような関数を高階関数といい、Scalaのライブラリでは関数オブジェクトを引数にもらうAPIが多く定義されています。

 実践的な例を見てみましょう。通常、Javaでファイルから読み込みを行う場合は、InputStreamオブジェクトやReaderオブジェクトを生成して、処理が終わった後はcloseメソッドを呼び出して閉じる必要がありました。

 このcloseメソッドを呼び出し忘れると、リソースの解放漏れがおこってしまいます。安全に読み込みを行う方法として、次のようなread関数を用意します。

[リスト6]ファイルから安全に読み込むためのread関数
// 引数に読み込むファイル名と、
// 読み込んだ1行を処理する関数オブジェクトをもらう
def read( in: java.io.InputStream , f:(String) => Unit ) = {
  try{
    // ファイルを読み込むBufferedReaderを生成する *1
    val reader = new java.io.BufferedReader( new java.io.InputStreamReader(in))
    // 最初の一行を読み込む *2
    var line:String = reader.readLine

    while( line != null ){ // すべての行をファイルから読み込むまで繰り返し *3
      f( line )            // 引数にもらった関数オブジェクトを呼び出す *4
      line = reader.readLine   // 次の行を読み込む *5
    }
  } finally{
    // BufferedReaderを閉じる *6
    in.close
  }
}

 このread関数は、第1引数にファイルを読み込むInputStreamオブジェクト、第1引数に読み込んだ1行の文字列を受け取って処理する関数オブジェクトを取ります。

 ファイルを1行づつ出力するには、次のように利用します。

[リスト7]read関数を利用してファイルの内容を出力する
scala> val in = new java.io.FileInputStream("test.txt")
in: java.io.FileInputStream = java.io.FileInputStream@59530fe3

scala> read( in, (l:String)=>{ println( l )} )
aaa
bbb
ccc

 では、read関数の中ではどのような処理を行っているのか見てみましょう。

 まず、*1ではファイルを読み込むのに利用するnew java.io.BufferedReaderオブジェクトを生成しています。

 *2では、BufferedReaderオブジェクトから最初の一行を読み込んで、変更可能なvar変数であるlineに代入しています。

 *3では、lineがnullになるまで繰り返し処理をさせています。

 *4がポイントです。ファイルから読み込んだ1行分の文字列が変数lineに格納されていますので、その内容を引数としてread関数の第2引数でもらった関数オブジェクトを呼び出しています。リスト7の例では、「引数の文字列をptintln関数で出力する関数オブジェクト」が渡されていましたので、1行づつ出力されているのです。

 *5は、次の行の読み込みです。

 *6は、すべての行を読み込み終わった後に、引数のInputStreamオブジェクトのcloseメソッドを呼び出しています。try{ ...} finallyで全体を囲んでいるため、何らかの例外が発生した場合でも、引数のInputStreamオブジェクトをcloseし忘れることはありません。

 このread関数を利用する側では、BufferedReaderオブジェクトの生成とcloseメソッドの呼び出しという処理を意識しなくても安全にファイルを処理できます。read関数の中に、これらのリソースを取り扱う処理が隠蔽されているからです。

 このように、リソースの生成や後処理を関数の中にまとめてしまい、リソースを利用した処理を行う関数オブジェクトをもらうような関数定義を、「Loanパターン」といいいます。

 「Loanパターン」は、ファイルの読み込みの他に、データベースのコネクションオブジェクトなど、後処理が必要なさまざまなリソースを処理するのに応用することが可能です。

カリー化と部分適用

 カリー化とは、複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」に変換することをいいます。

 具体的には、2つの引数を取って加算する関数があったとして、1つ目の引数を与えると、1引数を取って加算した結果を最初に与えた引数に加えて返す「関数」が返ります。

 Scalaでは、引数宣言の()を複数書くことができ、これで既にcurriedSum関数はカリー化されています。 では、どのように呼び出すのでしょうか。利用例は次のとおりです。

[リスト8]カリー化された関数
def curriedSum( n:Int )( m:Int ) = n + m

scala> curriedSum( 3 )( 5 )
res0: Int = 8

 呼び出すときも、宣言されている()と同じだけ(引数)を渡せばよいわけです。

 では、1番目の引数を与えて、「もとの関数の残りの引数を取り結果を返す関数」を得るにはどのようにするとよいのでしょうか?

 関数名に対してスペースを空けて「_」を指定することで関数オブジェクトを取り出すことができます。

[リスト9]カリー化された関数を取り出す
scala> val cf = curriedSum( 3 ) _
cf: (Int) => Int = <function>

scala> cf( 5 )
res1: Int = 8

 変数cfには、ちゃんと3に引数を加えて返す関数が入っていますね。

 また、2番目の引数を与えてカリー化された関数を得たい場合は、対応する引数に( _ :型)を指定します。

[リスト10]任意の引数でカリー化された関数を取り出す
scala> val cf2 = curriedSum( _ :Int )( 7 )
cf2: (Int) => Int = <function1>

scala> cf2( 5 )
res2: Int = 12

 このように、カリー化された関数の一部分だけ引数を与えて関数を取り出すことを部分適用と言います。

次のページ
カリー化による疑似制御構文の作成

この記事は参考になりましたか?

今からでも遅くない これから始めるScala連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 尾崎 智仁(オザキ トモヒト)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/5251 2010/07/09 15:48

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー