普段はC++でたまにpythonやったりPHPだったりでRuby自体は業務で使うことはないのですが、
Rubyも勉強してみるかと軽い気持ちでやっていたときにRubyならではの作法に関して
つらつらと書いていきます。
変数
Rubyの変数に関して。
・グローバル変数
どこからでも参照可能である変数。命名規則として頭に$をつける必要がある
1 2 3 4 5 6 |
$hoge = 1 def foo puts $hoge end foo puts $hoge |
C++でも使い方としては同じ感じですね。ただ名前は別に$をつけるとかはないですが。
・ローカル変数
スコープがブロックまたはメソッド定義の終わりまでで命名規則で先頭は英小文字、または_
さきほどのグローバル変数をローカル変数に変えると、
1 2 3 4 5 6 |
hoge = 1 def foo puts hoge end foo puts hoge |
上記のようになりますが、
prog.rb:3:in foo': undefined local variable or method
hoge’ for main:Object (NameError)
from prog.rb:5:in `’
と「そんな変数知らねーよ」と怒られます。当たり前と言われればそれまでですが。
1 2 3 4 |
hoge = 2 class Foo p hoge end |
当然クラスでもだめです。この変数がグローバルであればOKです。
では、
1 2 3 4 5 6 7 |
class Foo hoge = 2 def func puts hoge end end Foo.new.func |
はどうだろうか。クラス内部で宣言されているのでメンバとしてもっているからOK。
と思ったらこれもそんな変数知らん、と怒られます。
同じクラス内部なんだからわかるだろ!と思いきやクラス定義とメソッドはそれぞれ独立したスコープを持っているので
相互に参照できないとのこと。C++的にはメンバ変数となってどこからでもクラス内部であれば参照してほしいところですが
Rubyではあくまでもただのローカル変数。
当然グローバルにすればアクセスできますが..
・インスタンス変数
じゃあクラス内部で共通して使うようにするにはどうするかというとこのインスタンス変数。
先頭に@をつけることでインスタンス変数となります。
1 2 3 4 5 6 7 |
class Foo @hoge = 2 def func puts @hoge end end Foo.new.func |
じゃあ上記のようにすればOKだ..と思ったらエラーはでないけど2と表示されない..
初期化されていないとされていますね。
1 2 3 4 5 6 7 8 9 10 11 |
class Foo def func puts @hoge end def hoge=(value) @hoge = value end end a = Foo.new a.hoge = 2 puts a.func |
こんな感じでインスタンス変数をインスタンスメソッド内部で初期化してやるといけます。
1 2 3 4 5 6 7 8 9 |
class Foo attr_accessor :hoge def func puts @hoge end end a = Foo.new a.hoge = 2 puts a.func |
もう少し簡単に書くとこう。
attr_accessorがgetterとsetterのメソッドを両方生成するメソッドです。
ちなみにattr_readerでgetterメソッド、attr_writerでsetterメソッドなので二つ律儀に定義しても同じです。
・クラス変数
先頭を@@をつける。
これがC++にない、独特のものでそもそもクラスの変数であるならそれがメンバ変数なのでは?と思っていたが
1 2 3 4 5 6 7 8 9 10 11 |
class Foo @@hoge = 1 def func puts @@hoge end def hoge=(value) @@hoge = value end end a = Foo.new puts a.func |
上記の場合だと「あれ?インスタンス変数と変わらん」と思うが
1 2 3 4 5 6 7 |
a = Foo.new puts a.func a.hoge = 3 puts a.func b = Foo.new puts b.func |
こんな感じにすると最初は1,次は3,そして最後も3が出力されます。
つまり別のインスタンスであっても共有されるとのこと。
かなり強力なものだがいつの間にか誰かが変更してえらいことになりそうな感じだな?と思っていたら
アンチパターンとして使わないように、と書いてたりしてるので気軽に使うもんではなさそうではある。
定数
Rubyは書き換え可能。
何言ってんだお前は、と言われそうだがRubyの定数は書き換えができる。
1 2 3 4 |
A = 3 puts A A = 4 puts A |
こんな感じで。警告はでますがエラーではない。警告はでるので気付けるといえば気付けるが。。
一応Rubyにはfreezeというメソッドがありこれを使うと変更はできなくなるがこれは
破壊的メソッドであれば禁止できるが再代入であれば結局防ぐことはできない。
また、
1 2 3 |
def hoge A = 3 end |
だとエラーになります。
メソッドは複数回の実行が前提なので定数の初期化、値の更新はできないようになっているとのこと。
case式
Rubyのcase文、
1 2 3 4 5 6 7 8 9 |
a = 2 case a when 1 then p 1 when 2 then p 2 when 3 then p 3 end |
C++だとswitch~caseですがRubyはcase~whenで。
上記の例だと出力されるのは2のみ。C++だとbreak文がないと3も出力しますがRubyではフォールスルーはないのでこうなります。
演算子
だいたいどの言語でも同じと思いきややっぱり異なるのはある。
・UFO演算子
<=>
と書く演算子で比較の結果を数値で返すとのこと
1 2 3 |
p 1 <=> 2 p 1 <=> 1 p 2 <=> 1 |
左辺の値が右辺の値より大きければ1、等しい場合は0、左辺の値が右辺の値より小さい場合は-1を返します。
ほかの比較演算子では「.equal?」、「.eql?」、「==」など。
1 2 3 4 5 6 |
a = 1.00 b = 1 puts a.equal? b #=> false puts a.eql? b #=> false puts a == b #=> true |
上記のように==だと浮動小数点との比較でも同じと判断されるがeql?は別と判断。
これが文字の比較だと
1 2 3 4 5 6 7 |
s = "hoge" t = "hoge" puts s.equal? t #=> false puts s.eql? t #=> true puts s == t #=> true |
おなじhogeなのにequal?だと別と判定される。これはオブジェクトまで比較するのでsとtには同じhogeが入っていてもオブジェクトは別なのでfalseとなります。
しかしeql?はオブジェクトの判定ではないのでtrue判定。
この辺り数値と文字の比較方法を間違えるとえらいことになりそう。