Ruby のカリー化を日本語で説明してみる

日本のカレーは本場インドのとは違うけど、インドの人が食べても美味しいと思うらしい。。。

# カリー化
ここは、カレー屋です。（と、します）
「種類」と「個数」を指定すると注文(文字列)を返す「注文」手続きを作ってみます。

```rb
注文 = -> 種類, 個数 { "注文は、#{種類}カレーを#{個数}個です" }

p 注文.("ビーフ", 1)      #=> "注文は、ビーフカレーを1個です"
p 注文.("ポーク", 2)      #=> "注文は、ポークカレーを2個です"
p 注文.("ほうれん草", 3)   #=> "注文は、ほうれん草カレーを3個です"
```

Proc#curry を使って「注文」をカリー化してみます。

```rb
カリー化された注文 = 注文.curry
```

「カリー化された注文」に「種類」を適用して、それぞれの注文手続きを作ってみます。

```rb
ビーフカレーの注文     = カリー化された注文["ビーフ"]        # "ビーフ"を適用
ポークカレーの注文     = カリー化された注文["ポーク"]        # "ポーク"を適用
ほうれん草カレーの注文  = カリー化された注文["ほうれん草"]    #  "ほうれん草"を適用
```

使ってみます。

```rb
p ビーフカレーの注文.(1)     #=> "注文は、ビーフカレーを1個です"
p ポークカレーの注文.(2)     #=> "注文は、ポークカレーを2個です"
p ほうれん草カレーの注文.(3)  #=> "注文は、ほうれん草カレーを3個です"
```

以上。



# 部分適用
カリー化された手続きに引数の適用までするのが部分適用です。
「Proc#部分適用」メソッドを定義して、それで「チキンカレーの注文」手続きを作ってみます。

```rb
class Proc
  alias カリー化 curry           # curry に別名「カリー化」をつける
  
  def 部分適用(適用する引数)       # 「Proc#部分適用」メソッドを定義
    カリー化[適用する引数]
  end
end

チキンカレーの注文 = 注文.部分適用("チキン")    # (注)メソッドなので「()」で呼び出す
```

使ってみます。

```rb
p チキンカレーの注文.(5)          #=> "注文は、チキンカレーを5個です"
```

メソッドを定義しなくとも、下のように一文でもできます。

```rb
マトンカレーの注文 = 注文.カリー化["マトン"]

p マトンカレーの注文.(10)        #=> "注文は、マトンカレーを10個です"
```

以上。

#まとめ

```rb:currying_sample_in_japanese.rb
注文 = -> 種類, 個数 { "注文は、#{種類}カレーを#{個数}個です" }

カリー化された注文 = 注文.curry                  # カリー化

ビーフカレーの注文 = カリー化された注文["ビーフ"]    # 部分適用

p 注文.("ビーフ", 1)      #=> "注文は、ビーフカレーを1個です"
p ビーフカレーの注文.(1)   #=> "注文は、ビーフカレーを1個です"
```

変数名等に日本語を使ってない版は以下。

```rb:currying_sample.rb
order = -> kind, n { "注文は、#{kind}カレーを#{n}個です" }

curried_order = order.curry

beef_order = curried_order["ビーフ"]

p order.("ビーフ", 1)       #=> "注文は、ビーフカレーを1個です"
p beef_order.(1)           #=> "注文は、ビーフカレーを1個です"
```


(※ @riocampos さんからコメントをいただいたので追記します。)
以下は、いずれも Proc を呼び出す構文です。詳しくはマニュアルをご覧ください。

```rb
ビーフカレーの注文.(1)        # 構文
ビーフカレーの注文[1]         # Proc#[] は Proc#call の別名です
ビーフカレーの注文.call(1)    #         〃
```

- class Proc (Ruby2.1.0 リファレンスマニュアル) : http://docs.ruby-lang.org/ja/2.1.0/class/Proc.html

#おわりに
Ruby は変数名などに日本語が使えます。さすが国産言語です。
このメモはそれをネタに遊んでみたくて書いてみました。

スクリプトの動作確認は以下の環境で行っています。

- Ruby 2.1.5 (Ubuntu Linux 14.04)  

カリー化などの詳しいことは、以下の参考リンク、その他をご覧ください。

- カリー化 (Wikipedia) : http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%BC%E5%8C%96


#追記
ちょこっと、追記します。

##適用する引数の数が足りた場合
適用する引数の数が足りた場合は、部分適用されたProcでなく元のProcの実行結果が返ります。

```rb
sum = -> a, b, c { a + b + c }

sum1     = sum.curry[1]
sum1_2   = sum.curry[1,2]
sum1_2_3 = sum.curry[1,2,3]       # 引数の数が足りた

p sum1.(2,3)      #=> 6
p sum1_2.(3)      #=> 6
#p sum1_2_3.()    #=> NoMethodError!! (Proc でないので呼び出すとエラーになる) 
p sum1_2_3        #=> 6
p sum1_2_3.class  #=> FixNum
````

##部分適用したProcを引数不足で呼び出した場合
部分適用したProcを引数不足で呼び出した場合、部分適用されたProcが返ります。

```rb
sum = -> a, b, c { a + b + c }

sum1     = sum.curry[1]
sum1_2   = sum1.(2)       # 引数を二つとる sum1 を一つの引数で呼び出す

p sum1_2.(3)              #=> 6
```

（※投稿後ですが、書き漏らしていたので追記します。）
。。。(上記のよう)なので、以下の２種類の書き方はいずれも同じ効果になります。

```rb
sum1_2_3 = sum.curry[1,2,3]   # カリー化されたProcに一度に引数を渡す

sum1_2_3 = sum.curry[1][2][3] # 返ってきたProcに引数を渡す、を繰り返す
```

## Proc#curryの引数
Proc#curry の引数には arity(引数の数) を指定できます。
上のように引数の数が固定の場合はあまり意味がないと思います(上の場合 3 以外を指定するとエラーになる)が、可変引数をとるProcの場合は以下のように使えます。

```rb
sum = -> *ns { ns.reduce :+ }  # sum は可変引数をとる

p sum.()           #=> nil
p sum.(1)          #=> 1
p sum.(1,2)        #=> 3
p sum.(1,2,3)      #=> 6
p sum.(1,2,3,4)    #=> 10
p sum.(*1..10)     #=> 55
p sum.(*"a".."z")  #=> "abcdefghijklmnopqrstuvwxyz" (この例は、お遊び)

sum1     = sum.curry(3)[1]         # curry の引数で 3 を指定
sum1_2   = sum1.(2)
sum1_2_3 = sum1_2.(3)

                  # 結果は、元のProcが3引数だった場合と同じ
p sum1.(2,3)      #=> 6
p sum1_2.(3)      #=> 6
p sum1_2_3        #=> 6
```

## case 文の when 句やブロックで使う場合
Proc は case 文の when句や、メソッドにブロックとして渡せますが、カリー化されたものは渡す時に(部分的に)引数を渡せます。 

```rb
less = -> n, x { x < n }.curry

case 3
  when less[5] ; p "Yes"    #=> "Yes"
end

p [*1..9].select &less[5]   #=> [1, 2, 3, 4]
```
```rb
is_array = -> cond, x { x.kind_of?(Array) && x.all? {|e| cond === e } }.curry

case [1,2,3]
  when is_array[Integer] ; p "Yes1"           #=> "Yes1"
end

case [1,2,3]
  when is_array[(1..5)] ; p "Yes2"            #=> "Yes2"
end

case ["abc", "bbc", "ccc"]
  when is_array[/c/] ; p "Yes3"               #=> "Yes3"
end
```
```rb
match = :===.to_proc.curry(2) 
                           # Array#grep と同様の機能
p ["abc","aaa","bbc","ccc"].select &match[/a/]        #=> ["abc", "aaa"]
p [1,:foo,"aaa","bbc",{a:10}].select &match[String]   #=> ["aaa", "bbc"]
```