書いて覚えるSwift入門

第1回 関数型プログラミングを試す

この記事を読むのに必要な時間:およそ 2 分

関数という名の(定|変)

連載第1回目(もちろん0から数えています)の今回は,前回予告どおり関数型言語としてのSwiftを見ていきます。ところで「関数型言語」とはいったいなんでしょう。実のところあいまいな用語ではあるのですが,「最低限文化的な関数型」の関数は第一級オブジェクトであることに異議のある読者はあまりいらっしゃらないと思います。「第一級オブジェクトってなんぞや」おさらいしてみるとこういう感じでしょうか。

  • 変数に代入できる
  • 関数の引数にできる
  • 関数を返す関数が書ける

JavaScript,Lua,Perl,Python,PHP,Rubyといった今日日のスクリプト言語はその意味において「最低限文化的な関数型言語」の定義を満たしています。一例としてJavaScriptを見てみましょうか。前回のFizzBuzzを普通に書くとこんな感じでしょうかリスト1)。

リスト1 FizzBuzzの例

var fizzbuzz = function(n) {
    if (n % 15 == 0) { return "FizzBuzz"; }
    if (n % 5 == 0) { return "Buzz"; }
    if (n % 3 == 0) { return "Fizz"; }
    return n;
}
for (var i = 1; i <= 100; i++) {
     console.log(fizzbuzz(i))
}

見てのとおり,fizzbuzzは変数名で,そこに関数を代入しています。Swiftではどうでしょうか? こうかな?

var fizzubzz = func(n:Int)->String {
    if n % 15 == 0 { return "FizzBuzz" }
    if n % 5 == 0 { return "Buzz" }
    if n % 3 == 0 { return "Fizz" }
    return String(n)
}

いいえ,ちょっと違います。正しくはこうです。

var fizzbuzz = { (n:Int)->String in
    if n % 15 == 0 { return "FizzBuzz" }
    if n % 5 == 0 { return "Buzz" }
    if n % 3 == 0 { return "Fizz" }
    return String(n)
}

あるいはこうです。

var fizzbuzz:(Int)->String = { n in
    if n % 15 == 0 { return "FizzBuzz" }
    if n % 5 == 0 { return "Buzz" }
    if n % 3 == 0 { return "Fizz" }
    return String(n)
}

まとめるとこう。

funcは不要
  • { 引数 in 定義 }という形をしている
型指定は必要
  • →前者は{型定義付き引数 in 定義}
  • →後者は変数名:型 = { 引数名 in 定義 }

変数指定を{}の外ではなく中でやるあたり,JavaScriptよりむしろRubyっぽいですね。実はもっとRubyっぽいことをこれから見ていきます。FizzBuzzをちょっと変えて,「1から100に対応した結果を出力」するのではなく,「1から100まで入った配列をFizzBuzz変換」することを考えてみましょう。JavaScriptではこうかなリスト2)。

リスト2 JavaScriptの場合

function range(start, end) { // ないので作る
    var ret = [];
    for (var i = start; i <= end; i++) ret.push(i);
    return ret;
}
var a = range(1, 100).map(fizzbuzz)
console.log(a)

ここで,fizzbuzz()を無名化するとこうなります。

var a = range(1, 100).map(function(n) {
    if (n % 15 == 0) { return "FizzBuzz"; }
    if (n % 5 == 0) { return "Buzz"; }
    if (n % 3 == 0) { return "Fizz"; }
    return n;
}))

動くことは動きますが,()の中に入ったfunctionはなんとも不格好です。Rubyならどうでしょうか?

a = (1..100).map { ¦n¦
    if n % 15 == 0 then "FizzBuzz"
    elsif n % 5 == 0 then "Buzz"
    elsif n % 3 == 0 then "Fizz"
    else n.to_s
    end
}
p a

「メソッドの最後の引数がブロックの場合,()の外に書いてよい」というルールのおかげでずいぶんとエレガントです。それではSwiftでは?

var a = (1...100).map { n in
    if n % 15 == 0 { return "FizzBuzz" }
    if n % 5 == 0 { return "Buzz" }
    if n % 3 == 0 { return "Fizz" }
    return String(n)
}
println(a)

見てのとおり,mapの後の無名関数を()でくくらなくても動きました。無名関数の引数と戻り値のほうも指定していません。このあたりは,Swiftの父の1人であるChris Lattnerもホームページ「アイデアを拝借した」と率直に答えているとおりです。

しかし,Lattnerが言っていない,拝借されたアイデアがもう1つあります。コードで見てみましょうリスト3)。

リスト3 Rubyのアイデアを拝借

var a = (1...100).map {
    if $0 % 15 == 0 { return "FizzBuzz" }
    if $0 % 5 == 0 { return "Buzz" }
    if $0 % 3 == 0 { return "Fizz" }
    return String($0)
}
println(a)

なんと,inが消えてしまいました。その代わりnがあった位置に$0という未宣言の変数が存在します。この変数のことをプレイスホルダー(placeholder)変数というのですが,$0が最初の引数,$1が次の引数といった具合です。これ,どう見てもPerl 6のアイデアなんですが……。

著者プロフィール

小飼弾(こがいだん)

1969年生まれ,東京都出身。元ライブドア取締役の肩書きよりも,最近はPokemon GOのガチトレーナーのほうが有名になりつつある……かもしれない永遠のエンジニアオヤジ。

活躍の場はIT業界だけでなく,サブカルからアカデミックまで多方面にわたり,ネットからの情報発信は気の向くまま毎日毎秒! https://twitter.com/dankogai,ニコニコチャンネルは,http://ch.nicovideo.jp/dankogai,blogはhttp://blog.livedoor.jp/dankogai/

当社刊行書籍は『小飼弾のアルファギークに逢ってきた』『小飼弾のコードなエッセイ』など。他にも著書多数。

コメント

コメントの記入