SHOEISHA iD

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

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

アップルの新プログラミング言語「Swift」を探検しよう

Swiftのパワフルな記述力が秘められた
「関数」と「クロージャ」

アップルの新プログラミング言語「Swift」を探検しよう 第2回


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

auto_closure属性

 引数を取らず、何らかの値を返す関数(() -> T)を引数とするような関数やクロージャ式に対しては、引数宣言にauto_closure属性を用いることができます。auto_closure属性を用いた関数引数に対しては、次のような処理が行われます。

  • 単なる式に対してクロージャ式に使う括弧を自動的に被せるように変換される
  • 属性のついたパラメータに関しては評価を遅延させることができる

 例として、何も値を取らず、Bool値を返す関数を引数に持つ次のような関数を考えてみましょう。

func lazyAnd(lhs: Bool, rhs: () -> Bool) -> Bool {
    if lhs {
       return rhs()
    } 
    return lhs
}
lazyAnd(1 > 3, {1 < 3}) // false

 このlazyAnd関数は「&&」演算子を関数にしたものです。この関数宣言の関数引数に対して、auto_closure属性を適用してみましょう(属性の頭には「@」がつきます)。

func lazyAnd(lhs: Bool, rhs: @auto_closure () -> Bool) -> Bool {
    if lhs {
       return rhs()
    } 
    return lhs
}
lazyAnd(1 > 3, 1 < 3) // false

 lazyAnd関数を呼び出すときの末尾の引数から、{}カッコがはずれました。auto_closure属性を適用した引数に与えられた式(ここでは1 < 3)は、引数を持たないクロージャへ自動的に包み込まれるため、{}カッコをつけないのです。

 ところで、lazyAnd関数を呼び出している文を見ると、第2引数としてBool値(1 < 3を評価した結果のtrue)を受け取っているように見えます。しかし、lazyAnd関数が第2引数で受け取っているのは、式1 < 3を包み込んだクロージャです。そのため、式1 < 3は第1引数がtrueだったときに、lazyAnd関数内部のif文の中ではじめて評価されます(rhs()により)。

 もし、第1引数がfalseだったら、式1 < 3は一度も評価されません。つまり、auto_closure属性によって式をクロージャとしてキャプチャすることで、その評価を遅延させることができ、結果的に無駄な計算をカットすることが可能になります。

 クラス・ストラクチャのプロパティ宣言で用いることが可能なlazyキーワードについても、このような遅延の考え方を適用できますが、その解説は次回以降に行います。

ネスト関数クロージャによる値のキャプチャ

 ネストされた関数は値をキャプチャするため、注意が必要です。例として、次のような関数を考えます。

func makeStrAppender(str: String) -> () -> String {
    var acc = ""
    func appender() -> String {
        acc += str
        return acc
    }
    return appender
}

 makeStrAppender関数の戻り値はネスト関数appenderで、呼び出し元ではそれを値(関数型の値)として変数に代入できます。また、appender関数は、makeStrAppender関数内のacc変数をキャプチャしています。そのため、appender関数を代入した変数を使ってappender関数が呼び出されると、acc変数はその影響を受けます。

var appendPlus = makeStrAppender("+")
appendPlus() // "+"
appendPlus() // "++"
appendPlus() // "+++"

 関数を代入した変数に再度関数を代入すると、代入してあった関数インスタンスは破棄され、キャプチャされた値が初期状態に戻ります。

appendPlus = makeStrAppender("+")
appendPlus() // "+"

 関数、クロージャ式を含めたクロージャは参照型のオブジェクトとして扱われるため、代入時には関数インスタンスのコピーではなく、関数インスタンスへの参照が渡されます。その関数インスタンスがキャプチャした変数や定数も引き継がれます。

 次のコードを見てください。appendPlusRef変数には、appendPlus変数に代入されているappender関数への参照が渡されるため、appendPlusRef()によりacc変数は"++"になり、最後のappendPlus()によりacc変数は"+++"になっています。

var appendPlus = makeStrAppender("+")
appendPlus() // "+"
let appendPlusRef = appendPlus
appendPlusRef() // "++"
appendPlus() // "+++"

 

次回は

 今回は、Swiftの関数とクロージャを解説しましたが、いかがでしたでしょうか。関数型言語とスクリプト言語の“いいとこ取り”をしたような構文で、特にプログラミング言語好きな方は「おもしろい!」と感じたのではないでしょうか。やや難解ではありますが、その強力さはプログラミングの効率化に大きく寄与すると思います。

 次回は、やはりSwiftの特徴的な機能であるクラスとストラクチャ、列挙型のオーバービューをした後に列挙型とオプショナル列挙型を取り上げます。お楽しみに。

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

  • X ポスト
  • このエントリーをはてなブックマークに追加
アップルの新プログラミング言語「Swift」を探検しよう連載記事一覧

もっと読む

この記事の著者

yad(ヤド)

クラスメソッド株式会社のアプリケーションエンジニア。iPhoneアプリケーションの開発に2年以上従事している。開発に使用するObjective-Cのみならず、関数型言語Haskellや機械学習などにも関心がある。「Developers.IO」に寄稿した記事の一覧

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

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

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/7869 2014/07/09 21:05

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング