この講座は、Web業界未経験の吉野 桜と、その先輩である堀切 あやめのかわいさをお楽しみいただくため、邪魔にならない程度の差し障りのないWeb技術の話を、お楽しみいただく番組です
キャラクター
本編
すべてはオブジェクト
桜「Ruby何もわかんないですぅ」
あやめ「何もわからないってことは、チュートリアルをやって完全に理解して、いざ実践的な内容になると何もわからないっていう状況?」
桜「まさにそんな感じですぅ」
あやめ「桜ちゃんはRuby以外にどんな言語をこれまでやってきたんだっけ?」
桜「JavaScriptとPythonを少し」
あやめ「どっちも動的型付けのスクリプト言語だね。じゃあ、Rubyも分かるんじゃない?」
桜「それがあんまりしっくりきてなくて。まず文法が違うじゃないですかぁ?」
あやめ「文法は違ってもやってることは同じだからそんなに難しくないよ。プログラミング言語の文法の差なんて方言みたいなものだから。まあ、とりあえずRuby特有の思想から話していこっか」
桜「はい!」
あやめ「Rubyはね。すべてがオブジェクトなんだよ」
桜「それはオブジェクト指向っていうことですか? Pythonとかもオブジェクト指向の言語ですけど、それと何が違うんですか?」
あやめ「まあ、そうなんだけど。他の言語よりクラスの継承関係とかが見やすいんだよね。例えば、.class
でオブジェクトのクラスを見れて、.class.ancestors
で継承してるクラス、モジュールを一覧できるんだよね」
3.class
# Integer
3.class.ancestors
# [Integer, Numeric, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]
桜「なるほど。他の言語でもオブジェクトのクラスは見れますけど、継承関係が一覧できるのはいいですね!」
あやめ「Rubyではすべてが何かのクラスのインスタンスになるから、nil
もクラスを持っているし、Class
自身もクラスを持っているの」
nil.class
# NilClass
Class.class
# Class
BasicObject.class
# Class
桜「へー、ClassのクラスもClassなんですねぇ。なんだかゲシュタルト崩壊しそうですぅ」
あやめ「他にもオブジェクトに関して、そのクラスや持っているメソッドの情報を確認するメソッドがあるから覚えておいてね」
# 先祖に引数のクラスかモジュールが存在するか判定
object.is_a?(ClassOrModule)
# 親クラスのインスタンスか判定
object.instance_of?(Class)
# 保持するメソッドの一覧
object.methods
# メソッドを持つか判定
object.respond_to?(:method)
あやめ「ちなみに、Rubyではメソッドの呼び出し元のオブジェクトのことをレシーバって言うから覚えておいてね。上の例で言うと、objectがレシーバのことだね」
桜「レシーバですか、あんまり聞いたことがないです」
あやめ「元はSmalltalkから来てるみたいだね」
桜「Smalltalk、知らない子ですね……」
あやめ「2020年代からプログラミングを始める人は聞き馴染みがないのは仕方ないね」
Rubyにおける繰り返し
桜「Rubyのオブジェクトに対する考え方はなんとなく分かりました。他にもいろいろ疑問に思うところがあるんですよね」
あやめ「例えば?」
桜「なんかループを回したい時に、forループ以外にも、.times
とか(1..3)
みたいな謎のループ使ったりするじゃないですか? あれが謎なんですよね」
あやめ「あー。Rubyではあんまりforでループを回さなくて、他の方法で代用することが多いんだよね。例えば、普通に配列の要素を出力する時も、forじゃなく.each
を使うことが多いの」
arr = ["sato", "ito", "saito"]
arr.each do |e|
puts e
end
# sato
# ito
# saito
桜「あー、こんな感じですよね、Rubyって。他の言語だと基本的にはforでループ書きますもんね。じゃあ、.times
とかって何なんですか?」
あやめ「Integer型のメソッドで、レシーバの回数だけブロックを実行するんだよ」
桜「???」
あやめ「まず.times
のクラスを確認しよう」
3.times.class
# Enumerator
Enumerator.ancestors
# [Enumerator, Enumerable, Object, PP::ObjectMixin, Kernel, BasicObject]
桜「Enumerator
って何ですか?」
あやめ「Enumerableモジュールをインクルードしたクラスだね。Enumeratorってあんまり聞き馴染みがないかもしれないけど、他の言語で言うところのイテレータだね」
桜「イテレータならPythonで聞いたことあります! forループを回せたりするオブジェクトですよね」
あやめ「ざっくり言うとそうだね。一回、例を見てみよっか」
cnt = 0
3.times { cnt += 1 }
cnt # 3
桜「この{}
の中の処理がブロックなんですか?」
あやめ「そうだね。ブロックについては後で説明するけど{}
とdo end
で書かれた処理はほぼ同じ意味だよ」
桜「なるほど。じゃあ、ここではcntっていうカウンタ変数に3回1を足してるわけですね」
あやめ「その通りだね。他の言語みたいにfor (let i=0; i<3; i++)
なんて書かなくていいから楽でしょ?」
桜「たしかに! 退屈で共通化された記述を減らしてくれてるんですね。じゃあ、(1..3)
とかって何ですか?」
あやめ「これも一度クラスを確認してみよっか」
(1..3).class
# Range
(1..3).class.ancestors
# [Range, Enumerable, Object, PP::ObjectMixin, Kernel, BasicObject]
あやめ「なんか見覚えがあるものはない?」
桜「あります! ここでもEnumerableモジュールをインクルードしてますね!」
あやめ「そうなの。だから.times
も(1..3)
もforループを回せるし、.each
でループを回すことも可能なの」
桜「なるほど。じゃあこのRangeオブジェクトはどんなことができるんですか?」
あやめ「.each
を回したり、あと、配列をスライスする時にも使うね。ちなみに.
が3つつくと最後の要素は含まれないっていう違いがある」
(1..3).each do |e|
puts e
end
# 1
# 2
# 3
arr = [1, 2, 3, 4, 5]
arr[1..3]
# [2, 3, 4]
arr[1...3]
# [2, 3]
ブロック
桜「ところで、ブロックって結局何なんですか?」
あやめ「ブロックは他の言語ではあまり見かけないから、分かりづらいよね」
桜「はい。あれの正体が分からないんです」
あやめ「なるほどね。ブロックの正体は、メソッドで呼び出し可能なかたまりってところかな」
桜「ブロックもオブジェクトなんですか?」
あやめ「いや、ブロックは呼び出し可能なんだけどオブジェクトじゃないね」
桜「でも、Rubyってすべてはオブジェクトなんじゃなかったでしたっけ? なんでもオブジェクトだよーって聞きましたけど」
あやめ「なんでも、とは言ってない的なね」
桜「なんですか、そのアニメのお約束みたいな展開……とりあえず、ブロックは例外なんですね」
あやめ「うん。これまでに.each
は何回か出てきたよね。もう一度見てみよう」
[1, 2, 3].each do |e|
puts e
end
# 1
# 2
# 3
あやめ「例の通り、ブロックは呼び出し可能な引数として扱えるの。他の言語で言うとコールバック関数の役割を担っているね」
桜「あー、なるほど! .eachメソッドの引数にコールバック関数を渡してるんですね! JavaScriptで言うとこんな感じですね」
[1, 2, 3].map((e) => console.log(e))
// 1
// 2
// 3
桜「ちなみに、JavaScriptのmapはこんな感じで引数にインデックスも渡せますけど、Rubyはそういうのないんですか?」
[1, 2, 3].map((e, i) => console.log(e, i))
// 1 0
// 2 1
// 3 2
あやめ「あるよ。.with_indexでメソッドチェーンさせれば同じことができる。.eachだけじゃなくて.mapや他のEnumeratorクラスでも利用可能だね」
[1, 2, 3].each.with_index do |e, i|
puts e, i
end
# 1 0
# 2 1
# 3 2
桜「おー! ちゃんとあるんですね、感心しました!」
あやめ「それは何よりだよ。どう? ちょっとRubyが分かるようになってきた?」
桜「まあ、若干、ですかね」
あやめ「そうだよね。まだまだ覚えることは多いけど、地道にがんばろうね」
桜「はい!」