これだったら Ruby も関数型言語?

anopara.net
わたしは初級者プログラマですが、ここでの話は納得、というか、Ruby プログラマなら常識みたいな話でもあると思います。Ruby はふつうに書いて関数型プログラミングのエッセンスを抽出しているといわれることもありますが、Ruby関数型プログラミングと相性がよいことはあまり知られていないのではないでしょうか。って初級者がいうことですけれどね。いや、map とか select とか inject を使えって、ただそれだけのことで、Rubyist なら常識だと思います。

上のブログの C# コードを再掲しておきます。

var maleEmps = new List<Employee>();
for(var e in employeeList){
    if(e.gender == "M")
        maleEmps.Add(e);
}

「従業員リストの中から男性のみを抽出したい」というようなコードだそうです。
 

var maleEmps = new List<Employee>();
for(var e in employeeList){
    if(e.gender == "M" && e.salary > 4000000)
        maleEmps.Add(e);
}

男性社員で年収400万以上の人を抽出したい。
 

var names = new List<String>();
for(var e in employeeList)
    names.Add(e.name);

社員の名前のリストを抽出したい。
 

var sum = 0;
for(var e in employeeList){
    if(e.gender == "M")
        sum += e.salary;
}

男性社員の年俸の合計を計算しています。
 

Ruby で「関数型プログラミング」っぽく

では、男性のみを抽出してみましょう。Scala だと

val maleEmps = employeeList.filter(e => e.gender == "M")

となるのだそうです。ワンライナーですね。じゃあ Ruby だと、select を使って

male_emps = employee_list.select {|e| e.gender == "M")}

という感じですか(変数名は Ruby 文化のスネークケースで書いています)。Ruby だとふつうですね。

どんどんいきましょう。
男性社員で年収400万以上の人を抽出したい。

male_400over_emps = employee_list.select {|e| e.gender == "M" && e.salary > 400_0000}

 
社員の名前のリストを抽出したい。

emp_names = employee_list.map(&:name)

map を使用。ここではブロック変数が省略可能なので、さらにシンプルになっています。

男性社員の年俸の合計を計算。

sum = employee_list.select {|e| e.gender == "M"}.inject(0) {|acc, e| acc + e.salary}

inject(畳み込み)を使用。あるいは Ruby 2.4 以降だと、Array#sum を使って

sum = employee_list.select {|e| e.gender == "M"}.map(&:salary).sum

でも OK です。
以上、上サイトの Scala の場合とほぼ同じように書けているのがわかります。


つまり、ここで「関数型プログラミング」っぽいといっているのは、Ruby ではふつうの文化だということがわかります。Ruby のブロックは、高階関数を可読性の高い形で書くのにぴったりなのです。というのは、もちろん Rubyist には当り前の話なのですが。