Ruby 画像ダウンロード編 (8) ブロックの理解 【計算数学 I】

更新日時:

ブロックの理解

さていよいよ、コードの書き方に進みたい……ところですが、ブロックを理解して使う必要があります。 ブロックはとても便利です。Ruby では日常的に使います。 このブロックは、一度理解すればなんてことはないのですが、言葉で説明するのが難しいように思います。 ですから、ブロックをメソッドに渡すとはどういうことなのか、一節を割いて最初からきっちり説明します。

ブロックの書き方と実行

ブロックとは、以下の性質を持つものです。

  • ブロックとは、メソッドが 0 個か 1 個受け取るものです。
  • ブロックとは、命令の集まりです。(名前のない関数またはメソッドだと思っても概念上差し支えありません)
  • ブロックを受け取ったメソッドは、ブロックを任意の回数実行することができます。
  • ブロックを受け取ったメソッドは、ブロックに変数を渡すことができます。

4 番目は難しい(しかしここまで必要)ので、ひとまず 3 番目までの性質に絞って具体的に説明します。

ひとまず、テキストエディタで次のコードを書いて保存し、それを実行してください。

def shino
  puts "こんにちは、薗部篠と申します。西川家の元メイドです。"
  yield
  puts "三者三様の人生、ほんとおもしろ…ステキですよね。"
  puts "お聴きください、『ぐーちょきパレード』。じゃんけん、ぽん!"
end

shino {
  puts "「腹黒委員長」、「貧乏毎日パンの耳」、「胃袋ブラックホール」。"
}

注:テキストエディタによっては、# coding: utf-8 やそれに似たものが1行目に勝手に挿入されるかもしれません。これはこれで問題ないので気にしないでください。これは Ruby 処理系が受け取る文字コードを指定するものです。今のソースコードでは日本語が挿入されているので、エンコーディング形式が複数生じえます。 Ruby 2.0 以降ではデフォルトのエンコーディングは UTF-8 であるため、UTF-8 で書く限りはこの1行は不要です(他のエンコーディングを使う場合は書いておいたほうが安全でしょう)。実習用マシンは Linux の最新のディストリビューションを使用するため、大抵のテキストエディタではまずもって UTF-8 がデフォルトになるでしょう。結論を言うと、この1行は挿入されても挿入されなくても問題ありません。

このプログラムの解説をします。このコードで「実行」される部分は、最後の3行です。 下から3行目に書かれた shino で、その上に定義された shino というメソッドを呼び出しています。 shino は、直後に記述された { } の部分を受け取っています。この部分がブロックです。他に、ブロックの書き方として、

shino do
  puts "「腹黒委員長」、「貧乏毎日パンの耳」、「胃袋ブラックホール」。"
end

というように、doend で囲う方法もあります。この2つは、ブロックの効用としては完全に等価で、使い分けはプログラマに任されています。一例を挙げると

  • do end は手続きっぽいものとして使い、 { } は値を返すものを使う
  • do end は複数行にまたがるものに使い、 { } は1行で使う

という流儀があるようです。筆者は使い分けをせず、全部 { } で書くようにしています。皆さんはお好みでどうぞ。この教材では全部 { } で書きます。 厳密には、この2つは、メソッドの引数の ( ) を省略した際の結合の仕方が異なるのですが、気にしなくて良いです。

さてプログラムの解読にもどります。今回 shino に渡したブロックの中身は

  puts "「腹黒委員長」、「貧乏毎日パンの耳」、「胃袋ブラックホール」。"

です。これを念頭に置き、 shino というメソッドの定義を見てみましょう。 ここには yield という命令があることがわかるでしょう。yield は、受け取ったブロックをここで実行するという命令です。

以上の理解から、次のようになります。今回のプログラムでは、shino を呼び出していますから、実際の「実行」の内容としては

  puts "こんにちは、薗部篠と申します。西川家の元メイドです。"
  yield
  puts "三者三様の人生、ほんとおもしろ…ステキですよね。"
  puts "お聴きください、『ぐーちょきパレード』。じゃんけん、ぽん!"

が実行されます。ところが yield は、受け取ったブロックを実行するということでした。ですから、ブロックの中身を「代入」して

puts "こんにちは、薗部篠と申します。西川家の元メイドです。"
puts "「腹黒委員長」、「貧乏毎日パンの耳」、「胃袋ブラックホール」。"
puts "三者三様の人生、ほんとおもしろ…ステキですよね。"
puts "お聴きください、『ぐーちょきパレード』。じゃんけん、ぽん!"

という命令が、結局のところ、実行されているとわかります。ですから出力は当然、

こんにちは、薗部篠と申します。西川家の元メイドです。
「腹黒委員長」、「貧乏毎日パンの耳」、「胃袋ブラックホール」。
三者三様の人生、ほんとおもしろ…ステキですよね。
お聴きください、『ぐーちょきパレード』。じゃんけん、ぽん!

となるわけです。

ここまでで

  • ブロックとは、命令の集まりです。(名前のない関数またはメソッドだと思っても概念上差し支えありません)

についてはお分かりいただけたかと思います。

  • ブロックとは、メソッドが0個か1個受け取るものです。
  • ブロックを受け取ったメソッドは、ブロックを任意の回数実行することができます。

についても、容易に想像がつくでしょう。すなわち、ブロック { } はメソッドに多くても1つしか渡せません。yield は、メソッドの中に何回書いても良いです。

ブロックに渡す変数

それではレベルを1つ上げて

  • ブロックを受け取ったメソッドは、ブロックに変数を渡すことができます。
    • メソッドはブロックにどうやって変数を渡すのか?
    • ブロックはメソッドからどうやって変数を受け取るのか?

を解説したいと思います。2つの小項目を順に解説します。

まず前者からです。これはとっても簡単で、yield の引数として渡すだけです。yield は命令ですから、引数を取ることができます。 yield が受け取った引数が、メソッドがブロックに渡す変数です。 例えば yield("葉山", "ベル") と書けば、2つの文字列を引数としており、第 1 引数が "葉山" 、 第 2 引数が "ベル" です。 この結果、メソッドはブロックに "葉山", "ベル" という 2 つの変数を渡します。

次に後者です。ブロックの変数の受け取り方は、

{|name, cat|
  
}

のように書きます。つまり、2 つの縦棒 | | の間に、変数をコンマで区切って書きます。縦棒 | のことを、一般に「パイプライン」「パーティカルバー」と呼びます。 今の場合、 nameyield の第 1 引数が入り、 catyield の第 2 引数が入ります。

演習

問 1. 次のコードの空欄を埋めて、指定された出力結果を得るプログラムを完成させましょう。

コード

def cats
  yield("葉山", "ベル")
  yield("西山", "エル")
end

cats {|name, cat|
    # ここを埋める。
}

出力結果

葉山さんはベルというネコを飼っています。
西山さんはエルというネコを飼っています。

問 2. 問 1 の解答プログラムに手を加えて、次の出力結果を得ましょう。ただし、メソッド cats の定義を修正してはなりません。

出力結果

事実1:葉山さんはベルというネコを飼っています。
事実2:西山さんはエルというネコを飼っています。

ヒント:文字列の結合の仕方がわからない場合は、「文字列の結合」の節をもう一度読んで下さい。puts は1回だけ書けば良いです。Ruby では、整数を持つ変数に 1 を足す時に、インクリメントは使えません。 num = num + 1 または num += 1 のように書いてください。この 2 問は、上記の説明だけで解けますが、ややハードル高いかもしれません。試行錯誤してみてください。

ナビゲーション

この教材は、東京大学理学部数学科専門科目「計算数学 I」のために執筆されたものです。 このサイトに掲載する際に、記事を分けてあります。 他の回はRuby 画像ダウンロード編 一覧 #ks1-ruby-downloadから御覧ください。

Ruby 入門 (計算数学実習資料集)には他の TA が書いた教材があります。

コメントする