工藤探偵事務所

Resarch and Investigation

ルビー3分クッキング 第八回「繰り返しの不思議の繰り返し」の巻

kudo-shunsaku2008-04-08



3分間で覚える Ruby のトピックスです。
でも、本家同様に実際には10分程度掛かります。

大事なのは小さな積み重ねと継続する才能ですね。
一番の怠け者の俺が言うのも変ですけどね。


第八回は、「繰り返しの不思議の繰り返し」の巻です。

事前に前号である第七回をお読み下さいますと少し流れが見えるかもしれません。
ですが、話はしょっちゅう彼方此方に跳んでしまうので此処だけで充分かもしれません。これまたいつもの事ですので御容赦下さいませ。


実は前回、嬉しいことに初めての反応を頂きまして、しかも御質問まで頂けたので喜んでお答えしようと想ったのも束の間、未熟者故に上手くお応え出来ませんでした。なので今回はその御質問を食材にしました続編になります。

頂いたご質問は、

『ブロックは "{...}" (ブレース)ではなく、"do...end" で囲むことも出来ます。
 (実は、ちょっとだけ意味が違います。)
 とありますが、これら2つの書き方でどこら辺の意味が違うのでしょうか??』

この鋭く的確質問に驚愕しておりました。。。実は前号を書いてる際に、この部分ちょっと説明しづらいなぁと思い倦ねておりまして、それにとりあえず今説明しなくても良いかな?って具合で「(実は、ちょっとだけ意味が違います。) 」と誤魔化したのです。


では簡潔に説明を試みますと『 "{...}" (ブレース、波括弧)と "do...end" の違い 』は、「結合力」の違いの様子です。結合する力は「優先度」の影響に由来します。ですから複雑な式の場合には書き方によっては、双方で結果が異なったり、文法エラーになったりもします。

ということで早速、試してみましょう。

先ずは、 "do...end" でブロックを書きました。

ファイル名は、"brace_do_end.rb" などとして下さい。

#! ruby -Ks

# 炒めますよ、メソッド。
def saute(ingredient)
  if block_given?
    puts "『 #{yield} 』をソテーします。"
  else
    puts "『 #{ingredient} 』をソテーします。"
  end
end

# 食材です。変数です。
ingredient = "玉葱"

# 炒めてみましょう。
saute ingredient do
  "人参"
end

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby brace_do_end.rb
『 人参 』をソテーします。

C:\rubycode\3minutes2cooking>

実行結果を見てください。

結果は、ブロックで指定した『人参』がソテーされました。

解説は後ほどで。

では続きまして今度は、"{...}" (ブレース、波括弧)で指定してみましょう。

先ほどのソースコードをちょこっと書き換えます。

#! ruby -Ks

# 炒めますよ、メソッド。
def saute(ingredient)
  if block_given?
    puts "『 #{yield} 』をソテーします。"
  else
    puts "『 #{ingredient} 』をソテーします。"
  end
end

# 食材です。
ingredient = "玉葱"

# 炒めてみましょう。
saute ingredient {
  "人参"
}

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby brace_do_end.rb
brace_do_end.rb:16: undefined method `ingredient' for main:Object (NoMethodError)

C:\rubycode\3minutes2cooking>

今度はエラーになってしまいました。

このエラーは"{...}" (ブレース、波括弧)でブロックを指定しているからです。
直前の変数である "ingredient" とくっ付いたからなのです。ですから何某かのブロック処理をしてくれるメソッドかと思いきや、ただの変数だったのでエラーになってしまった様子です。

では、先ほどの "do...end" でブロックを書いた場合はちゃんと実行出来ましたね。
これは急いで目の前の変数に飛びつかず、メソッド全体とくっ付いたのでちゃんとブロックが評価されたのです。その結果、ブロックで返した "人参" という文字列をメソッドが印字してくれたのです。

このソースコードでは両社の違いを確かめるために、ブロックを引数に取れるメソッドを定義してあります。この "saute" (ソテー)メソッドはブロックが来ればブロックから食材を貰いますし、ブロックが無ければ引数を食材として炒めます。メソッド定義と条件分岐については、またいつかご紹介しますね。

そして Ruby では、メソッドを呼び出す際に取る引数を囲む括弧 "()" を省略出来ます。馴染みの組み込みメソッドである "p" や "puts" も括弧省略して使ってますよね。ですから、こういった書き方が出来るのです。便利ですが、あまり省略し過ぎると可読性が落ちますけどね。

折角ですので、今度は両方どちらのブロックの指定を採用してもエラーにならないように少し工夫してみます。

もう一度先ほどのソースコードを書き換えてみましょう。

#! ruby -Ks

# 炒めますよ、メソッド。
def saute(ingredient)
  if block_given?
    puts "『 #{yield} 』をソテーします。"
  else
    puts "『 #{ingredient} 』をソテーします。"
  end
end

# 食材です。今度は簡単なメソッドにしてあります。
def ingredient
    "玉葱"
end


# 「do...end」で指定したものを炒めます。
saute ingredient do
  "人参"
end

# 「波括弧」で指定したものを炒めます。
saute ingredient {
  "人参"
}

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby brace_do_end.rb
『 人参 』をソテーします。
『 玉葱 』をソテーします。

C:\rubycode\3minutes2cooking>

両方、ちゃんと実行出来ましたね。でも、結果が異なりますね。

"{...}" (ブレース、波括弧)がエラーになった部分をクリアするために、今度は食材をメソッドに書き換えているのです。実行結果を見ても判りますが、先にブロックを評価してしまったために、印字されるのは "玉葱" ですね。


余談ですが、だからと言って "do...end" の方が良いかどうかは一概には言えないと考えます(但し、慣習的なものかもしれませんが、一部にはこちらを指示する方もいらっしゃる様子です)。自身にとっては、"{...}" (ブレース)の方が馴染み深いですし、なんといっても視認性(可読性)の点で有利であるからです。一般にも観やすいと考えます。どちらを使うのかに関しては適時でしょうが、ある程度自分のスタイルが固まれば自然に行えるような気がします。それまで自分が習熟できるのかは疑問ですけど(笑)。

またここで揚げた良い例(サンプル)が適切か否かはちょっと怪しいのですが、何某かの理解の糸口に繋がれていればと願います。御容赦願います。

そうそ、メソッド定義と条件分岐にの話はまたいつか。

そうそ、それと、もし質問に答えられなくても御容赦下さいね。




「たらこー♩たらこー♩つ〜ぶぅつ〜ぶぅ♫たらこー♩」

今日はここまで。次回をお楽しみに。