公式ドキュメントのやつ。1時間はかかった。
成果物
情報源
REPL
端末を起動してirb
コマンドを実行する。
irb
Ruby用REPLが起動する。
irb(main):001:0>
Hello World
irb(main):001:0> "Hello World" => "Hello World" irb(main):002:0> 'Hello World' => "Hello World"
クォーテーションはシングルでもダブルでも良さそう。違いはあるのだろうか。
でもクォートをとったらエラーになった。
irb(main):003:0> Hello World (irb):3:in `<main>': uninitialized constant World (NameError) from /home/pi/.anyenv/envs/rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.3.5/exe/irb:11:in `<top (required)>' from /home/pi/.anyenv/envs/rbenv/versions/3.0.2/bin/irb:23:in `load' from /home/pi/.anyenv/envs/rbenv/versions/3.0.2/bin/irb:23:in `<main>'
World
は初期化されていない語ですよ。ってことかな?
え、じゃあHello
は初期化されているの? ていうか初期化ってなに? たぶん変数宣言のことだろうが。
エラーメッセージは微妙だな。
puts
puts
関数。Ruby的にこれは関数なのか? それともメソッド? あるいはマクロ定義?
irb(main):005:0> puts "Hello World" Hello World => nil irb(main):006:0> puts("Hello World") Hello World => nil
関数のカッコは省略できる。試してみる! (ブラウザから)で学んだ。昔のPython2系と同じ。Python3では省略できなくなったはずだが。まあどちらでも書けるほうがいい、のかな? 省略されたほうが見やすい。
print
との違いも気になる。そのうち調べよう。
nil
はたしかC言語でいうNULL
やPythonでいうNone
と似たような概念だったはず。たぶんputs
関数の戻り値がないことを意味しているのだろう。
計算
irb(main):007:0> 3+2 => 5 irb(main):008:0> 3 + 2 => 5 irb(main):009:0> 3*2 => 6 irb(main):010:0> 3**2 => 9 irb(main):011:0> Math.sqrt(9) => 3.0
irb(main):012:0> a = 3**2 => 9 irb(main):013:0> b = 4**2 => 16 irb(main):014:0> Math.sqrt(a+b) => 5.0
関数
定義
irb(main):020:1* def hi irb(main):021:1* puts "Hello World !!" irb(main):022:0> end => :hi
関数名を忘れてしまったら、以下のように怒られた。
irb(main):015:1* def irb(main):016:1> puts "Hello World!!" irb(main):017:0> end /home/pi/.anyenv/envs/rbenv/versions/3.0.2/lib/ruby/3.0.0/irb/workspace.rb:116:in `eval': (irb):16: syntax error, unexpected string literal, expecting ';' or '\n' (SyntaxError) puts "Hello World!!" ^ (irb):17: syntax error, unexpected `end', expecting end-of-input from /home/pi/.anyenv/envs/rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.3.5/exe/irb:11:in `<top (required)>' from /home/pi/.anyenv/envs/rbenv/versions/3.0.2/bin/irb:23:in `load' from /home/pi/.anyenv/envs/rbenv/versions/3.0.2/bin/irb:23:in `<main>'
構文エラーなのはわかったけど「関数名がない」とはっきり原因を言ってほしい。このエラーメッセージだと全然わからん。;
だの\n
だのputs "Hello World!!"
だの、すべて的外れ。
呼出
irb(main):023:0> hi Hello World !! => nil
カッコをつけても呼べる。
irb(main):024:0> hi() Hello World !! => nil
引数
irb(main):025:1* def hi(name) irb(main):026:1* puts "Hello #{name} !" irb(main):027:0> end => :hi
テンプレート・リテラルの構文も判明した。"#{変数名}"
らしい。
呼び出してみる。
irb(main):028:0> hi "山田" Hello 山田 ! => nil
オプション引数
irb(main):034:1* def hi(name="World") irb(main):035:1* puts "Hello #{name.capitalize} !" irb(main):036:0> end => :hi
引数の初期値をWorld
にした。
ついでにテンプレート・リテラル構文? でメソッドを呼んでいる。capitalize
は先頭1文字目を大文字にするメソッドだと思われる。
呼び出してみる。
irb(main):037:0> hi "yamada" Hello Yamada ! => nil
カッコなしで呼び出せるのいいわ。タイプ数が少なくて楽。
class
irb
に以下をコピペする。
class Greeter def initialize(name = "World") @name = name end def say_hi puts "Hi #{@name}!" end def say_bye puts "Bye #{@name}, come back soon." end end
すると以下のようになる。
irb(main):038:1* class Greeter irb(main):039:2* def initialize(name = "World") irb(main):040:2* @name = name irb(main):041:1* end irb(main):042:2* def say_hi irb(main):043:2* puts "Hi #{@name}!" irb(main):044:1* end irb(main):045:2* def say_bye irb(main):046:2* puts "Bye #{@name}, come back soon." irb(main):047:1* end irb(main):048:0> end => :say_bye
@name
はインスタンス変数。たぶん@
はC#でいうthis
であり、Pythonでいうself
なのだろう。思えばPythonはself
地獄だったが、Rubyにはない。かわりにend
地獄だが。
インスタンス生成
文書にはオブジェクトと書いてあった。たしかRubyってすべてがオブジェクトなんだっけ? まあいいや。C/C++, C#, Javaと勉強してきた私にはインスタンスのほうがわかりやすい。とりあえずRubyの用語定義についてはそのうち調べよう。
irb(main):049:0> g = Greeter.new "Pat" => #<Greeter:0x009eaf48 @name="Pat">
メソッド呼出
irb(main):050:0> g.say_hi Hi Pat! => nil irb(main):051:0> g.say_bye Bye Pat, come back soon. => nil
インスタンス変数は参照できない
irb(main):052:0> g.@name /home/pi/.anyenv/envs/rbenv/versions/3.0.2/lib/ruby/3.0.0/irb/workspace.rb:116:in `eval': (irb):52: syntax error, unexpected instance variable (SyntaxError) g.@name ^~~~~ from /home/pi/.anyenv/envs/rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.3.5/exe/irb:11:in `<top (required)>' from /home/pi/.anyenv/envs/rbenv/versions/3.0.2/bin/irb:23:in `load' from /home/pi/.anyenv/envs/rbenv/versions/3.0.2/bin/irb:23:in `<main>'
クラス定義したときに作った@name
。でも生成したインスタンスからは参照できないんだってさ。
カプセル化されているということか。すばらしい! ここはPythonと違う。いいね。単一責任を果たすコードが書けそう。
instance_methods
クラスメソッドのinstance_methods
を使えば見える。とドキュメントには書いてあったが、@name
が見当たらない。まあ、だってメソッドでなく変数だもんね。
irb(main):054:0> Greeter.instance_methods => [:say_bye, :say_hi, :pretty_print_cycle, :pretty_print_inspect, :pretty_print_instance_variables, :pretty_print, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :class, :frozen?, :then, :public_send, :method, :public_method, :singleton_method, :tap, :define_singleton_method, :extend, :clone, :yield_self, :to_enum, :enum_for, :<=>, :===, :=~, :!~, :nil?, :eql?, :respond_to?, :freeze, :inspect, :object_id, :send, :to_s, :pretty_inspect, :display, :hash, :singleton_class, :dup, :itself, :!, :==, :!=, :equal?, :instance_eval, :instance_exec, :__id__, :__send__]
どうやらGreeter
が継承しているすべてのクラスのメソッドも含めているようだ。たぶんすべてのクラスはベースとなるオブジェクトクラスを継承している、とかそういうことなんだろう。
引数にfalse
を渡せば、そのクラスで定義したものだけが出る。
irb(main):055:0> Greeter.instance_methods false => [:say_bye, :say_hi]
respond_to
どのメソッドに反応するかを調べるらしい。
末尾の?
をつけ忘れたら以下のように怒られた。
irb(main):056:0> g.respond_to("name") (irb):56:in `<main>': undefined method `respond_to' for #<Greeter:0x009eaf48 @name="Pat"> (NoMethodError) Did you mean? respond_to? from /home/pi/.anyenv/envs/rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/irb-1.3.5/exe/irb:11:in `<top (required)>' from /home/pi/.anyenv/envs/rbenv/versions/3.0.2/bin/irb:23:in `load' from /home/pi/.anyenv/envs/rbenv/versions/3.0.2/bin/irb:23:in `<main>'
このエラーメッセージは対処がそのまま書いてあって素晴らしい。でも、そう思うんだったら?
ついていなくてもそれを実行してくれたらいいのに。
ていうか、?
ってなんぞ? そういう名前の変数なの? それともNULL周りの処理をする特殊な構文とか? わからん。
irb(main):057:0> g.respond_to?("name") => false irb(main):058:0> g.respond_to?("say_hi") => true irb(main):059:0> g.respond_to?("say_bye") => true irb(main):060:0> g.respond_to?("to_s") => true irb(main):061:0> g.respond_to?("@name") => false
attr_accessor
インスタンス変数を公開するときはattr_accessor
を使う。
class Greeter attr_accessor :name end
あと、これで既存のGreeter
クラスを変更できてしまったらしい。なにそれ怖い。間違って名前重複しちゃっていても気づかないとか、ありそう。
:name
のように:
を先頭につけたやつをシンボルというらしい。このへん、あんまり説明がないのでよくわからない。今はそういうものだとしておく。詳細はここを参照。
irb(main):068:1* class Greeter irb(main):069:1* attr_accessor :name irb(main):070:0> end => [:name, :name=]
インスタンス生成する。
irb(main):072:0> g = Greeter.new "Andy" => #<Greeter:0x00772920 @name="Andy">
name
メソッドがある。
irb(main):073:0> g.respond_to?("name") => true irb(main):074:0> g.name => "Andy"
name
を参照しているメソッドでちゃんと反映されている。
irb(main):075:0> g.say_hi Hi Andy! => nil
name
を変更して、反映されることを確認する。
irb(main):076:0> g.name = "Betty" => "Betty" irb(main):077:0> g.say_hi Hi Betty! => nil irb(main):078:0> g.name => "Betty"
おそらくattr_accessor
は、C++やC#でいうpublic
アクセス修飾子のようなものなのだろう。デフォルトではprivate
だけど、attr_accessor
にセットされたものはpublic
になる感じか。
irbを終了する
quit
コードをファイルに書く
a.rb
#!/usr/bin/env ruby class MegaGreeter attr_accessor :names # Create the object def initialize(names = "World") @names = names end # Say hi to everybody def say_hi if @names.nil? puts "..." elsif @names.respond_to?("each") # @names is a list of some kind, iterate! @names.each do |name| puts "Hello #{name}!" end else puts "Hello #{@names}!" end end # Say bye to everybody def say_bye if @names.nil? puts "..." elsif @names.respond_to?("join") # Join the list elements with commas puts "Goodbye #{@names.join(", ")}. Come back soon!" else puts "Goodbye #{@names}. Come back soon!" end end end if __FILE__ == $0 mg = MegaGreeter.new mg.say_hi mg.say_bye # Change name to be "Zeke" mg.names = "Zeke" mg.say_hi mg.say_bye # Change the name to an array of names mg.names = ["Albert", "Brenda", "Charles", "Dave", "Engelbert"] mg.say_hi mg.say_bye # Change to nil mg.names = nil mg.say_hi mg.say_bye end
実行権限を付与する。
chmod +x a.rb
実行する。
a.rb
Hello World! Goodbye World. Come back soon! Hello Zeke! Goodbye Zeke. Come back soon! Hello Albert! Hello Brenda! Hello Charles! Hello Dave! Hello Engelbert! Goodbye Albert, Brenda, Charles, Dave, Engelbert. Come back soon! ... ...
コメント
#
ではじまる行はコメントである。
# これはコメントです
if文
if
, else
, elsif
, end
。
elsif
が嫌だ。else if
でもelif
でもなくelsif
。これは初めてのパターンだ。キモチワルイ。
?
末尾につく?
が謎。
if @names.nil?
elsif @names.respond_to?("each")
メソッド名末尾 | 意味 |
---|---|
? |
メソッド名の一部。慣用的に真偽値を返すメソッド名を示す。 |
! |
メソッド名の一部。慣用的に破壊的な作用をもつメソッドに使う。(渡された引数のうち配列の内容を書き換えるなど。! がない同メソッドはコピーされた別のインスタンスを返す) |
おそらく!
は、C#でいうref
修飾子。?
はPythonでいうis_xxxx
のような真偽値をreturn
するときのメソッド名の命名規則だと思われる。
Rubyはシンプルに表現できて素晴らしい。大抵のプログラミング言語は!
や?
の字を名前に使えない。正規表現でいうと[a-zA-Z_][a-zA-Z0-9_]*
のはず。Rubyは違うようだ。
each
@names.each do |name| puts "Hello #{name}!" end
do
とend
が{}
の意味らしい。{}
に固有の名前をつけるのやめてほしい。忘れるんだよ。英語わかんないんだよ。{}
にしてほしかった。
蛇足
C#っぽく書くと以下。絶対こっちのほうが見やすい。
@names.each(name) => { puts "Hello #{name}!" }
でも()
省略したいから以下のほうが嬉しい。
@names.each => name { puts "Hello #{name}!" }
一行のときは以下で書けたら嬉しい。
@names.each => name puts "Hello #{name}!"
動的型づけ
names
の型はstring
でもarray
でも動く。
# names = string mg = MegaGreeter.new mg.names = "Zeke" mg.say_hi # names = array mg.names = ["Albert", "Brenda", "Charles", "Dave", "Engelbert"] mg.say_hi
name
の型によって処理内容を変えている。
def say_bye if @names.nil? puts "..." elsif @names.respond_to?("join") puts "Goodbye #{@names.join(", ")}. Come back soon!" else puts "Goodbye #{@names}. Come back soon!" end end
配列だったらjoin
メソッドがあるはず。なのでそのメソッド存在判定をしている。elsif @names.respond_to?("join")
で。
型ではなく、メソッドが存在していることをもって判断する。ダッグ・タイピングというやつである。
エンドポイント
if __FILE__ == $0 end
キーワード | 意味 |
---|---|
__FILE__ |
ソースコードファイルパスを返す。 |
$0 |
実行時の最初の引数(ソースコードファイルパス) |
このif
文はなくても実行できる。でもたぶん、これがないとimport
したときに実行されてしまうのだろう。Pythonのときでもそうだった。そういえばRubyにおけるimport
はどうやってやるんだろう? モジュールなどの概念についても知らない。
エンドポイントは、Pythonだったら以下のように書いていた。
if __name__ == '__main__':
所感
20分では終わらなかった。1時間はかかった。いつもこういうとき「私って無能なんだ。ノロマなんだ……😢」とおもって悲しくなる。「すぐにできるよ!」とアピールして読者を増やそうという目論見は理解できるけど、メンタルケアまではやってくれない。そういうアフターフォローって大事よ? というわけで、私は私を慰めてみる。
大丈夫だよ。こういうのって短い時間で終わるアピールしているだけだから! 彼らにとって好都合な小さい数字を出しているだけだよ。それに、色々調査せずにやっていたらきっとそのくらいで終わっていたって。だから大丈夫。決してノロマなわけじゃないよ。
……むなしい。初学の時点で才能のなさを突きつけられた感。
まあでも、Pythonよりはずっと書いていて楽しかった。なんかPythonて書いててイライラするんだよね。たぶんRubyでもend
が自動挿入されなかったらキレると思う。irb
ではend
が自動挿入されたり、インデントをよしなにやってくれたから良かった。
テキストエディタで書いたらやってくれないよね。本番はこれからだ。
対象環境
- Raspbierry pi 4 Model B
- Raspberry Pi OS buster 10.0 2020-08-20 ※
- bash 5.0.3(1)-release
- Ruby 3.0.2 ※)
$ uname -a Linux raspberrypi 5.10.52-v7l+ #1441 SMP Tue Aug 3 18:11:56 BST 2021 armv7l GNU/Linux