せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】ブロック、クロージャー、イテレーター

なんでブロックを使うのかなー
って話を勉強会でした時、ブロックのことがわかってなさそうなのがバレたのか、
メールをもらったよ。

 

郡司です。
思い出したのでついでに。
昔、自分のブログに Ruby のブロックについてまとめたのでご参考までに。
(ブロック、クロージャ、イテレータ
http://rubyist.g.hatena.ne.jp/muscovyduck/20060518/p1
http://rubyist.g.hatena.ne.jp/muscovyduck/20060519/p1
http://rubyist.g.hatena.ne.jp/muscovyduck/20060520/p1

なんと!
郡司さんありがとう!


 

クロージャー

一部変えたけれども、↓

def counter_closure
  count = 0
  lambda {|n| count += n }
end

count = 10
p counter_closure.call(1)
p counter_closure.call(2)
p counter_closure.call(3)
p counter_closure.call(4)
p "---"
counter = counter_closure
p counter.call(1)
p counter.call(2)
p counter.call(3)
p counter.call(4)

■実行結果

1
2
3
4
"---"
1
3
6
10

うんうん。

リテラル記法

「ブロック」はリテラルじゃないから、単にブロックだけをスクリプトに書いてもエラーになるよ。

http://rubyist.g.hatena.ne.jp/muscovyduck/20060518/p1

言われたら確かに。
ブロックにリテラル記法はない。

リテラルやリテラル記法については以前考えたところ


 

「手続き」?

ブロックは、パラメータをつけることで「手続き」だけでなく「関数」のようにふるまうことができるよ。

うーん・・・?
手続き・・・値を返さない
関数・・・値を返す
という理解なんだけれど。
ブロックは値を返すから関数だと思うけれど、パラメーターの有無は関係ないのではないかな。。?

「手続き」「関数」の概念を理解できていないのかもしれない。

関数渡し等々、関数型が理解できていないことは自覚があるので、
もう少ししたらJavaScriptの勉強を通して「関数」を理解する予定。
 →理由が見えてきたので追記。
 

追記

そらはさんからコメントをいただいたよ。
f:id:sekaiya:20131025220155j:plain
びっくり!ありがとう!

ふむふむ。。。
確かにブロックが返す値を使うことも使わないこともある。
分かりやすい説明だねー。さすがだね。
でも私が躓いているのはパラメータ云々のお話。

せっかくコメントももらったし、もう少し考えてみよう。
うーん。。

 
こんな時の師匠だね。。!
メールしてみよう。

師匠師匠、質問させてください。

とあるブログ(http://rubyist.g.hatena.ne.jp/muscovyduck/20060518/p1)に、
こういう一文がありました。
「ブロックは、パラメータをつけることで「手続き」だけでなく「関数」のようにふるまうことができるよ。パラメータは「|」で定義するよ。」
 
自分はこの内容を読んだときに、
「ブロックは値を返すから関数だと思うけれど、パラメーターの有無は関係ないのではないかな。。?」と思いまして(http://sekai.hateblo.jp/entry/2013/10/24/230921)。
 
そしたら、そらはさんにコメントを頂いたんですね。
「「ブロックは必ず値を返す」けれど、その返り値は使われないこともあり、ただ手続きだけを記述するのにつかうこともあるよね、という考えだとどうでしょうか」、と。
 
たしかにおっしゃるとおりです。
ただ、このコメントってパラメータ云々の内容には触れていないですよね?
私が理解できてないのはその点なんです。

現在、以下のような認識なのですが、ずれているところはどういう所でしょうか。。
 
■用語について
手続き・・・値を返さない
関数・・・値を返す
 
■存在しうるパターン
ブロックが手続き型として定義されており、引数を持つ(1)
ブロックが手続き型として定義されており、引数を持たない(2)
ブロックが関数型として定義されており、引数を持つ(3)
ブロックが関数型として定義されており、引数を持たない(4)
 
■コードの例
(1)
[1,2,3].each{|i| p i}

(2)
3.times{p Time.now}

(3)
a = [1,2,3].map{|i| i*i}

(4)
Array.new(3){Array.new(1,0)}
 
■結論
「パラメータをつけることで「手続き」だけでなく「関数」のようにふるまうことができる」とあるが、パラメーターの有無と「手続き」・「関数」のような振る舞いは独立している。

 
返事が来たよー。

ここでいう手続きとか関数ってRuby で正式な定義のある語ではないと思います
(「手続きオブジェクト」とか「関数のように」という語はありますが)

つまりは、「あまり意味のない議論」に感じます。
確かに他の言語では返り値の有無で呼び方を変えるものはありますが
Ruby の場合、常にあるので、そもそもこのような議論に意味がないと思います
パラメータも同様に思います。

結論:オレオレ定義でみんな語ってるから、気にするな

 

パラメーター云々で本質が変わらないから、議論に意味がないということですか?

 

そもそも、「関数」か「手続き」か という議論に意味がないです。
どっちもオレオレ定義でしかないので。
猫の鳴き声は「ニャー」か「にゃー」のどっちだ!?
というくらいに意味がないと思います

ひょー。
 

「関数」か「手続き」か、に意味のある言語は、たとえばどういうものがあるのですか?

 

delphi  とか pascalとか
用語はずれますが、fortranとか。

そっかー。

とりあえず大きくイメージがズレてることはなさそう。
今はこのレベルの理解で落ち着いておこう。


・・・・・・「にゃー」かな。


 

付属ブロックの呼び出し

pr = Proc.newとしたあとにループの中でpr.callとするのはちょっとカッコ悪いよね。

http://rubyist.g.hatena.ne.jp/muscovyduck/20060520/p1

 
たしかに。なんか難しいなーって思う。

そこで、記事ではyieldを使ったコードが紹介されていた。

class MyArray < Array
  def another_my_iterator
    i = 0
    while i < self.size
      yield self[i]
      i += 1
    end
  end
end

なるほど!


 
でも私は明示的にブロック引数を記述するほうが好きだな。
そのほうが分かりやすいと思うな。どうだろう?

class MyArray < Array
  def my_iterator(&b)
    self.size.times do |i|
      b.call(self[i])
    end
  end
end
fruits = MyArray.new
fruits.push('apple').push('mikan').push('ichigo')
fruits.my_iterator {|fruit| puts fruit }

 

ふむふむ。