LoginSignup
20
19

More than 5 years have passed since last update.

"Rubyistに贈るHaskell入門"がちょっと微妙だったので補足記事その1

Last updated at Posted at 2014-12-02

ども@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オブジェクトを生成するコストが高いこと
  • より、構文っぽく見える

からかなぁと思ってます。なんとなく。

20
19
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
19