ども@nobkzです、この記事
http://qiita.com/techno-tanoC/items/1549d0efc044faf16c36
がすごーく微妙だったので、個人的に追記していきたいと思います。
注意:Rubyあんまり書いたことが無いのでお手やわらかに
ちょっと長くなりそうなので、回を分けます
微妙だと思ったので追記したその1
blockとProcオブジェクトについて
まずはですね、先の記事では、高階関数を、「ブロック渡しの関数」と「Procオブジェクトを返す関数」を作ってましたけど、それはそれで正しいのですが、もうすこし、ブロックとProcオブジェクト、Haskellのラムダ式に関して若干の記述の足りなさを感じていたため、追記します。
blockはオブジェクトでは無いということ
まず、blockはオブジェクトでは無いということです 。これはどういうことかと言えば、もし、blockがオブジェクトであるならば、他のオブジェクトと同様に扱えるべきです。例えば、もしblockがオブジェクトであれば、代入ができるわけで、次の様な構文が可能であるべきです。
block = { puts "I am a block!"} # 不可
Haskellの関数
Haskellは関数はファーストクラスであって、ふつうの値と同様に扱うことができます
let id = (\x -> x)
blockをオブジェクト化 => Procオブジェクト
しかし、blockをオブジェクト化することは可能であり、それがProcオブジェクトであるわけです。Procオブジェクトは、blockを、変数のスコープやスタックフレームをオブジェクト化したものです。
proc_obj = Proc.new { puts "I am a Proc Object!" }
proc_obj.call #=> outputs : I am a Proc Object!
Procオブジェクトにおける高階関数
なので、Procオブジェクトは、ふつうのオブジェクトとして使えるため、引数として使えます。
def call_only p
p.call
end
call_only(Proc.new { puts "I am a Proc Object!" })
もちろん、返り値としても使えます。
def return_proc
Proc.new { puts "I have returned!" }
end
return_proc().call
これにおいて、Procオブジェクトを用いて、Rubyは高階関数に相当する記述が可能であるわけです。つまり、Rubyにおいて「関数を引数にする関数」というのは「Procオブジェクトを引数にしたメソッド」であり、「関数を戻り値にする関数」は「Procオブジェクトを返すメソッド」であるわけです 。
blockは、制限された「関数を渡す関数」
さて、Rubyにおいて、blockを渡すメソッドつまり、「block付きメソッド」というものがあります。先の記事においても紹介されていました。
先の記事では、高階関数における「関数を渡す関数」として、「block付きメソッド」を使ってました。それは、僕としても、間違ってはいないとは思います。
ちなみに、blockを引数にする関数はこうです。
def only_call_b &block
block.call
end
# yieldを使ってこうも記述できる
def only_call_b
yield
end
only_call_b { puts "I am a block" }
block付きメソッドというのは、
- blockは一つだけ
- blockは最後の引数
という制限があります。(だからyieldという記法も可能なわけですが)この点において、Haskellのラムダ式による高階関数としては、差異が見られます。どういうことかと言えば、Haskellはこれと同様な制限が無い ということです。
例えば、次のような関数を作ることが可能であるわけです。
Prelude> let sample x y cond = if cond then x . y else y . x
Prelude> sample (+1) (*2) True 1
3
Prelude> sample (+1) (*2) False 1
4
このようは関数をblock付きメソッドで作ることは不可です。
## 不可だが、強引に書いてみる
def sample(&x, &y, cond)
if cond then ->(n){ x.call(y.call(n)) } else ->(n){ y.call(x.call(n)) } end
end
## 不可だが、強引に書くとしたらこう?
sample {|x| x + 1}, {|x| x * 2 }, true
さて、これを実装するには、「Procオブジェクトを渡すメソッド」として高階関数を作ってあげれば良いのです。
def sample(x, y, cond)
if cond then ->(n){ x.call(y.call(n)) } else ->(n){ y.call(x.call(n)) } end
end
sample(->(n){ n + 1 }, ->(n){ n * 2 }, true).call(1)
sample(->(n){ n + 1 }, ->(n){ n * 2 }, false).call(1)
さて、まとめると、
- block付きメソッドは、「関数を一つだけ引数にして、かつ、最後の引数とする関数」という高階関数に相当するものであろうということ
- もし、Haskellと同等の高階関数に相当するものは、**「Procオブジェクトを引数にしたメソッド」「Procオブジェクトを返り値にしたオブジェクト」**がRubyにおける高階関数であるということです。
更に追記:そもそも、なんでblock付きメソッドがあるのか?
まぁ、これは詳しいソースは分かりませんが、おそらく
- Procオブジェクトを生成するコストが高いこと
- より、構文っぽく見える
からかなぁと思ってます。なんとなく。