概要
スクリプト言語的なものとしては、もっぱらPython
を使っていたが、
このごろRuby
を初めてややガッツリ触る機会があった。
すぐ慣れるだろうと甘くみていたら、予想以上にクセが強くて苦戦したので、
Python
使いの視点からRuby
の特徴を簡単にメモっておきたいと思う。
まだまだ勉強不足なので網羅的な説明にはならないと思うが、
最低限これだけ把握すれば大体不都合なくRuby
を読み書きできるというポイントをまとめる。
省略文化
Python
と似たようなものだろうと思っていた自分にまず面食らわせてきたのがこれ。
イカした特徴だと思うが、慣れないと大変。
returnの省略
return
は省略可。
例: ゲッターメソッド
def name
@name
end
メソッドの最後の行とは限らない。
例: case文
def hoge(arg)
case arg
when A
"Hoge"
when B
"Fuga"
else
"Foo"
end
end
途中で強制的にreturn
させる場合
def hoge(arg)
return "Hoge" if arg.nil?
"Fuga"
end
Ruby
のコーディング規約として、return
の省略は行わない方が良いという文献もあった。
return
省略カッコいいけど、可読性とか保守性の面で気にする人も少なくなさそう。
チームや製品の文化に合わせることが大事そう。
メソッドの括弧省略
引数ゼロのメソッドの宣言/コールは丸括弧を省略する。
def hoge
"Hoge"
end
p hoge
これによってゲッターメソッドが自然に書けるという、結構イカした仕様。
class Person
def initialize(name)
@name = name
end
def name
@name
end
end
person = Person.new("Alice")
p person.name
実際は自動プロパティ的な機能attr
系があるので、そちらを使うことの方が多いかも。
そこら辺は後述。
コールの際は引数があってもカッコを省略することがある。
命令風に書きたい時に使われるのかな?
以下は全く同じ。
hoge "Hoge", "Fuga"
hoge("Hoge", "Fuga")
メソッドオブジェクト
さて、メソッドのカッコを省略できると聞いてまず思うのは、
じゃあ関数ポインタどう書くの?
ということ。
以下のようにシンボルを使って書き、コールにはcall
メソッドを使う。
def hoge(arg)
p arg
end
fuga = method(:hoge)
fuga.call("hello")
このmethod(symbol)
によって生成されるオブジェクトをメソッドオブジェクト
とよぶ。
後述するProcオブジェクト
との違いが非常に難しい。。。
ディクショナリ(ハッシュ)のカッコ省略
最後の引数がハッシュの際はハッシュの波括弧を省略できる。
それだけなんだけど、、これがなんて恐ろしい、、、
def hoge(hash)
p hash["key1"]
end
hoge("key1" => "value1")
hoge({"key1" => "value1"})
hoge("key1" => "value1", "key2" => "value2")
hoge({"key1" => "value1", "key2" => "value2"})
# キーがシンボルの場合
hoge(key1: "value1")
hoge({key1: "value1"})
hoge(key1: "value1", key2: "value2")
hoge({key1: "value1", key2: "value2"})
ハッシュとシンボルについての説明は後述。
最初見た時ラムダ式でも渡しているのかと思ってた。。。
恐ろしいのは上例の後半の書き方。
この戦慄がお分かり頂けるだろうか、、、
# キーワード引数
def hoge(hoge: "a", foo: "b")
p hoge + foo
end
hoge(hoge: "a", foo: "b")
# ハッシュ引数
def hoge(**args)
p args[:key1]
end
hoge(key1: "value1", key2: "value2")
Ruby
の引数の受け渡しにおける壮大な思想の一面が見えた気がする、、
普通の引数も、キーワード引数も、ハッシュ引数も、
渡す側からしたら区別がつかない ということ。
区別する必要がない方が本質的だろ?
とRuby
に語りかけられているみたい、
他の人がどう考えているのかと思って少し調べたら、
とても分かりやすく引数についてまとめてくださっている記事があった。
こういう仕様ならばハッシュ引数**args
はなぜ必要なのかと疑問に思ったが、
多分以下のように特定のキーワード引数と分けて定義をするのに必要。
def hoge(a, b: "v1", **args)
# omit
end
# 特定のキーワード引数を分ける必要なければこれでいい
def hoge(a, hash)
# omit
end
とにかく、この最後の引数のハッシュは波括弧が省略可能
という仕様は、
非常によく考えられた深い仕様のように感じる。
命名規則
大体Python
と同じかなと。
術語メソッド(predicate method)の?
bool返すメソッドは名前の末尾を?
にして、
その分無理に疑問系にしない。
つまり、is_
とかhas_
とかは基本つけない。
あくまでメソッド名の話。変数名の話ではない。
# good
def connected?
# omit
end
# bad
def is_connected?
# omit
end
一見センス良く見えるが、このせいで以下のような不都合が出ているのかなとも。。。
# 三項演算子: ?が2回続いて紛らわしい
a = hoge.connected? ? "a" : "b"
# null条件演算子: ?はメソッド名に使うからRubyでは&なのかな
hoge&.close
破壊的メソッドの!
破壊的メソッドとそうでないメソッドの両者がある場合は、
破壊的メソッドの方の末尾に!
をつける。
これはセンスが良いと感じた。
class Point
def initialize(x, y)
@x = x
@y = y
end
def origin_symmetry
Point.new(-@x, -@y)
end
def origin_symmetry!
@x *= -1
@y *= -1
end
end
ただしこれはあくまで慣習であり、!
がついていないから安心と思ってはいけない。
(↑コメントでご指摘頂きありがとうございました!)
制御構文
unless
ifの反対。これを利用してできるだけ条件をシンプルにすることが求められる。
# good
loop do
break unless hoge
end
# bad
loop do
break if !hoge
end
ifとunlessの後置
すでに例で何度も出しているが、以下のように一行で書ける。
# good
loop do
break unless hoge
end
# bad
loop do
unless hoge
break
end
end
Python
に慣れているとあまりやりたくならないかもだが、
Ruby
だと一行にできるときはそうした方が良いと考える人が多そうなイメージ。
繰り返し
-
continue
ではなくてnext
-
繰り返しにおける
Python
のようなelse
は無い。 -
for-in(for-each)
values.each do |v|
p v
end
- 集合処理系
# 写像
[1, 2, 3, 4].map {|v| 2 * v}
# => [2, 4, 6, 8]
# 絞り込み
[1, 2, 3, 4].select {|v| v % 2 == 0}
# => [2, 4]
[1, 2, 3, 4].reject {|v| v % 2 == 0}
# => [1, 3]
[1, 2, 3, 4].find {|v| v % 2 == 0}
# => 2
ブロック, yield, Procオブジェクト, proc, lambda
モジュールと並んで最初最も難しい概念と思う。
以下参考にさせていただきました。
ここでは、自分なりに要点を整理しようと思います。
ブロック
do end
や{}
で囲っているよく見るアレ。
|a, b|
のようにして引数を受け取れる: ブロック変数
ブロック単体はメソッドに渡すために使う。
Procオブジェクト
に包むことでオブジェクト化でき、無名関数のように扱える(後述)。
どちらも基本は同じ。
(厳密には引数に渡す時に挙動の違いがあったりする)
使い分けの目安としては、
- 一行で収まるときは
{}
、複数行はdo end
- 後続でメソッドチェーンするときは
{}
ブロックとyield
Python
のyield
とは主従が逆!
- メソッドはデフォルトでブロックを1つ受け取ることができる
-
method(args) block
という感じになっている - 通常はブロックを受け取ることについて明示する必要はない
-
ブロック引数
を用いて明示的に受け取ることも一応できる(後述)
-
- 受け取ったブロックを
yield
で呼び出す -
yield
で渡された値がブロック変数に来る(複数可能, 受け取らなくてもいい)
1行ずつ読んでyieldする例
def read_lines
io = FileIOMock.new
loop do
line = io.read_line
break if line.nil?
yield line
end
end
read_lines {|line| p line}
んー、遅延評価とかメソッドチェーンとかやりにくい気がする、、、
動作確認用に作ったMock
class FileIOMock
def initialize
@lines = [
"hoge",
"fuga",
"foo"
]
@i = 0
end
def read_line
line = @lines[@i]
@i += 1 unless line.nil?
line
end
end
Procオブジェクト
-
Ruby
における無名関数のようなもの。 -
method(symbol)
で作ったメソッドオブジェクト
に似て非なる存在。 -
proc
とlambda
の2種類に分けられる。
メソッドオブジェクト
と対比させるなら、これはブロックオブジェクト
みたいなもの。
ブロックを直接メソッドに渡さず、オブジェクトとして扱いたい時に用いる。
proc
2通りの生成方法
Proc.new (block)
proc (block)
my_print_proc1 = Proc.new {|v| p "Hello #{v}"}
my_print_proc2 = proc {|v| p "Hello #{v}"}
lambda
my_print_lambda = lambda {|v| p "Hello #{v}"}
ブロック引数
メソッドと受け渡しするブロックを、Procオブジェクトとして扱うためのもの。
意外と紛らわしいので、焦らずちゃんと理解した方が良い。
受け取り側
定義側で引数の最後に&
をつけてブロック引数
を1つだけ定義することができる
すると、渡されたブロックをProcオブジェクト
としてメソッド内で扱える。
先述したyield
の例は、yield
を使わずに以下のように書ける。
def read_lines(&block)
io = FileIOMock.new
loop do
line = io.read_line
break if line.nil?
block.call(line)
end
end
read_lines {|line| p line}
メソッドコール側に変化はない。
渡す側
逆に、ブロックではなくてProcオブジェクト
をメソッドに渡したい場合は、
コール側で引数の最後に&
をつけてブロックとして渡すことができる。
def read_lines
io = FileIOMock.new
loop do
line = io.read_line
break if line.nil?
yield line
end
end
my_print_proc2 = proc {|v| p "Hello #{v}"}
read_lines(&my_print_proc2)
メソッド定義側に変化はない。
ちなみに、この方法でメソッドオブジェクト
も同様に渡すことができた。
(後述するto_proc
メソッドが呼ばれてProcオブジェクトに変換されていると思われる)
to_proc
メソッドと&
(コメントで教えて頂き追記しました)
渡す側で&
をつけて渡す際には、渡したオブジェクトのto_proc
メソッドが呼ばれる。
元々Procオブジェクトを渡した際には何の意味もないが、
メソッドオブジェクトや、その他to_proc
メソッドを実装しているオブジェクトに効果がある。
class Hoge
def to_proc
proc {p "hoge"}
end
end
def fuga
yield
end
fuga(&Hoge.new)
# => "hoge"
この仕様のおかげで、シンボルやハッシュも&で渡して利用できる。
ちなみに、to_proc
はおそらくproc
化するのでなくて、Procオブジェクト
化するためのメソッド。
実際にproc
になるのかlambda
になるのかは実装次第と思われる。
シンボルをProcオブジェクト化して、特定メソッドをコールさせる
シンボルのto_proc
は、第一引数にそのメソッドのレシーバを受け取って自身をコールさせるようなProcオブジェクトになる。
これにより、シンボルを&
で渡すことでyield
される各要素の特定のメソッドをコールさせる記述をスッキリ書ける。
["hoge", "", "fuga"].reject(&:empty?)
# => ["hoge", "fuga"]
# これと同じということ
["hoge", "", "fuga"].reject {|v| v.empty?}
# => ["hoge", "fuga"]
:empty?.to_proc
は、{|receiver| receiver.empty?}
のようなものになっていそう。
使い所
シンボルとかハッシュとか、Procオブジェクト化して使えるのは面白いし便利そう。
それ以外だと、慣れていない自分にとってはちょっと難しい。
紛らわしいが、普通に無名関数を渡したい文脈では、
ブロック引数
なんて意識せず普通に以下のようにすればいい。
def read_lines(line_process)
io = FileIOMock.new
loop do
line = io.read_line
break if line.nil?
line_process.call(line)
end
end
my_print_proc2 = proc {|v| p "Hello #{v}"}
read_lines(my_print_proc2)
デフォルトのブロックの受け渡しは1つしかできないので、
複数のコールバックを渡す必要がある場合にもこのような方法になるだろう。
ていうか、いつもこうすればブロック引数
だとかyield
だとかに混乱しなくて済むのでは?
そんなこと言ったら人類の進化は止まっちゃいますね。
せっかく綺麗に書ける仕組みがあるので、コールバックが1つの時は積極的にyield
使おう。
そうなると、複数メソッドを跨いでコールバックを受け渡す必要がある場合に、
ブロック引数が必要になってくるはず。
両側のブロック引数の使い方を盛り込んだ例
def read_lines(&block) # もらったブロックを子メソッドに跨がせたいのでprocとしてもらう
io = FileIOMock.new
loop do
line = read_inner(io, &block) # 子メソッドのブロックへprocを渡す
break if line.nil?
end
end
def read_inner(io)
line = io.read_line
yield line unless line.nil?
line
end
def line_process(time)
case time
when "morning"
proc {|v| p "Good morning #{v}"}
when "night"
proc {|v| p "Good night #{v}"}
else
proc {|v| p "Hello #{v}"}
end
end
read_lines(&line_process("morning")) # 渡す処理を動的に変更したいのでprocとして渡したい
例外処理
基本形
begin
rescue => e
ensure
end
メソッド全体にかける場合はbegin
とend
は省略できる
def hoge
p "main process"
raise "just for test"
rescue => e
p "error occurred: #{e.message}"
ensure
p "this line is always called"
end
特定の例外を捕捉しretry
する例
(retry
はrescue
句の中でしか使えない)
count = 0
begin
case count
when 0
raise IOError, "fuga"
when 1
raise "foo"
else
p "success!"
end
rescue IOError => e
p e
count += 1
retry
rescue => e
p e
count += 1
retry
end
tips
-
raise
には例外クラスとメッセージを別々に渡す- 次のように例外インスタンスを渡すのは良くないらしい
raise IOError.new("msg")
- 理由は前者の方が実はbacktraceに関する第三引数が省略可能で隠れていることにあるらしい
- ではメッセージだけ渡すケースについてはどう考えれば???
- ちなみに例外クラスとしては自動で
RuntimeError
になるらしい
- ちなみに例外クラスとしては自動で
- 次のように例外インスタンスを渡すのは良くないらしい
-
Exception
クラスはrescue
してはダメ-
rescue
して良い最も標準的なクラスはStandardError
- 実は
rescue => e
=rescue StandardError => e
- 実は
-
-
ensure
句の中でreturn
してはダメ- 例外が潰されてしまう
- 実は
ensure
句の中ではreturn
省略は無効。明示的にreturn
を書かなければ大丈夫。
参考: https://techracho.bpsinc.jp/hachi8833/2017_03_21/37192
クラス
インスタンス変数
-
@
を先頭につける - クラス外からは参照できない
クラスメソッド、クラス変数、クラスインスタンス変数
static系。
- クラスメソッド: 先頭に
self.
をつける-
クラス名.
でも良いが、冗長で好ましくないだろう
-
- クラス変数: 先頭に
@@
をつける - クラスインスタンス変数: staticに宣言したインスタンス変数
- クラスもClassクラスのインスタンスらしい、、
- クラス直下やクラスメソッド内でインスタンス変数を定義するとこれになるっぽい
staticなメンバ変数が普通に使えれば満足なんだけど、、
クラス変数とクラスインスタンス変数って、、
実はクラス変数は全てのサブクラスと共有される仕様らしい(子クラスに値を書き換えられる?)。
一方でクラスインスタンス変数は継承とは無関係。
可読性を考えたらできるだけクラス変数の方を使いたいように思うが、
確かに全てのサブクラスで共有っていうのも嫌だなあ。
クラス直下のローカル変数宣言とか処理とか
Python
に慣れている人なら、以下のように思うだろう。
クラス変数は先頭に@@
つけるならば、
ローカル変数をクラス直下に宣言したら何が起こるのか?
Python
はクラス直下に宣言した変数がクラス変数になるし。
クラス直下のローカル変数宣言や処理はクラスのロード時に走るだけで、
特に変数はクラスに紐付かずに破棄される。
クラスのロード時の処理を普通に書けるのは便利かも。
Python
もできたっけ??
定数
- アルファベット大文字 ([A-Z]) で始まる識別子は定数
- 定数は名前空間上(module, class上)に定義し、そこに保持される
- メソッド内では定義できない
- 外部からは
::
でアクセス - 内部からはインスタンスメソッド、クラスメソッドの双方から直接アクセスできる
定数、変数、メソッド全部のせ
class Hoge
# クラスロード時のローカル変数。ロード後破棄される。
local_var = "local var"
# クラスロード時に一度だけ実行される
p local_var
# 定数。名前空間上に保持される。
# 外部からはHoge::CONSTANT_VARのようにアクセス
# 内部からは直接アクセス可能
CONSTANT_VAR = "constant var"
# クラス変数
@@class_var = "class var"
# クラスインスタンス変数
@class_instance_var = "class instance var"
# クラスメソッド
def self.class_method
p "start class method"
p @@class_var
p @class_instance_var
p CONSTANT_VAR
p "end class method"
end
def initialize
#インスタンス変数
@instance_var = "instance var"
end
# インスタンスメソッド
def instance_method
p "start instance method"
p @instance_var
p CONSTANT_VAR
p "end instance method"
end
end
Hoge.class_method
Hoge.new.instance_method
p Hoge::CONSTANT_VAR
# => "local var"
# => "start class method"
# => "class var"
# => "class instance var"
# => "constant var"
# => "end class method"
# => "start instance method"
# => "instance var"
# => "constant var"
# => "end instance method"
# => "constant var"
アクセサ
自動プロパティ。
class Hoge
attr_accessor :rw # getter, setter
attr_reader :r # only getter
attr_writer :w # only setter
def initialize(a, b, c)
@rw = a
@r = b
@w = c
end
end
アクセス修飾子
メソッドはデフォルトでpublic。
privateキーワード以降が全部privateになる(wow!)
以下は fuga, hoo がprivateメソッドになる。
class Hoge
def hoge
end
private
def fuga
end
def foo
end
end
可読性、、保守性、、どうしてこうなった、、
(コメントでご指摘いただきました!
クラスがシンプルならばこれが便利と考える方も多そうです。)
Ruby 2.1 からは普通に以下のようにメソッド毎に書けるらしい。
class Hoge
# 言わずもがなpublic
def hoge
end
# これだけprivate
private def fuga
end
# これはpublic
def foo
end
end
絶対これを徹底した方が良いと思うのだけれども、
なぜかこうしていないコードをよく見る気がする。
(コメントを頂いたので以下追記)
これについては考え方が分かれそうです。
確かにクラスがシンプルであれば、まとめてprivateにしてしまう旧来のやり方も便利かもしれません。
自分は今まで当たり前のようにメソッド毎にprivateを付与してきたので、ちょっと慣れないですが、
チームや製品の文化に合わせるのが良さそうですね。
Rubyのprivateは実は曲者
privateメソッドは子クラスから呼び出せる(後述)。
Ruby
のprivateは レシーバから呼び出せない という仕様らしい(ただしself.
からは可能)。
なるほど、確かにそれなら一見privateっぽい挙動に見えるわけだ。
その他
細かい癖のある仕様が多くてまとめきれないので、ざっくり
- privateの時点で子クラスから呼べるというのに、protectedがあるらしい
- クラスメソッドをprivateにするには
private_class_method
なるものでシンボル指定しないといけないらしい
interface, abstract
ない。Python
と同じ。
オーバライド
- コンストラクタ、アクセサも含めインスタンスメソッドは全て継承されるし、オーバライドできる
- ただ同名で再定義するだけ
-
super
で親メソッドをコールできる
- 親のprivateメソッドも使える
- クラスメソッドも継承される
- クラス変数は共有されるので注意が必要
- クラスインスタンス変数は全く継承されない
- 定数は継承される、オーバーライドもできる
class Person
def initialize(name)
@name = name
end
def greet
say "こんにちは。"
end
private
def say(msg)
p "#{@name}: #{msg}"
end
end
class CheerfulPerson < Person
def greet
super
say "今日もいい天気ですね。"
end
end
class SilentPerson < Person
def greet
say "...。"
end
end
Person.new("Alice").greet
CheerfulPerson.new("Bob").greet
SilentPerson.new("Chris").greet
# => "Alice: こんにちは。"
# => "Bob: こんにちは。"
# => "Bob: 今日もいい天気ですね。"
# => "Chris: ...。"
特異メソッド
インスタンスに対して定義したメソッド。
class Hoge
end
hoge = Hoge.new
def hoge.special
p "hello"
end
hoge.special
# => "hello"
インスタンスに紐づくメソッドのように見えるが、
やっぱりメソッドは特定のクラスに紐づかないといけないということで、
この時には内部で専用のクラスを自動生成しているらしい。
そのクラスのことを特異クラスと呼ぶ。
後述するmixinをextendする際に特異メソッドを意識する。
モジュール
ブロックと並んで最初最も難しい概念の1つと思う。
名前空間としてのモジュール
これは簡単。
入れ子になったクラスも名前空間を形成する。
module Hoge
class Fuga
module Foo
class Poo
def poo
p "poo"
end
end
end
end
end
Hoge::Fuga::Foo::Poo.new.poo
# => "poo"
staticクラスとしてのモジュール
モジュールは実はstaticクラス、つまりインスタンス化できないクラスのような性質がある。
- module内でクラスメソッドを定義することで、外部から参照させられる
- 定数は名前空間上に公開される
module A
CONST_VAR = "constant var"
def self.hoge
"hello"
end
end
p A::CONST_VAR
# => "constant var"
p A.hoge
# => "hello"
一応module_function
というmoduleのインスタンスメソッドを公開する方法もあるが、
mixinをマスターした上級者向けの話と思う。
mixinとしてのモジュール
moduleがインスタンス化できないクラスならば、
module内のインスタンスメソッドは何に使えるのか?
その答えが、mixin。
Python
は多重継承でmixin可能だが、Ruby
は多重継承できない。
そのためRuby
ではモジュールを使ってmixinを実現する。
モジュールをmixinする方法は、下記の3種類。
- include
- 自身の親クラスにモジュールを挿入する
- モジュールを継承するイメージ
- prepend
- includeの逆で、モジュールに自身を継承させるイメージ
- 自クラスの実体がモジュールにスイッチし、その親クラス側に自クラスがくる感じ
- extend
- モジュールのインスタンスメソッドをselfの特異メソッドとして追加する
- クラスに対してextendした場合はクラスメソッドとして追加される
- インスタンスに対してextendした場合はインスタンスメソッドとして追加される
なんとモジュールがモジュールをmixinすることもある。
イルカとサメという異なる継承関係のクラスに、「泳ぐ」という共通処理をmixinする例。
(個人的にどこかで読んだイルカとサメのmixinの例がわかりやすくて好きです)
class Mammal
end
class Fish
end
module Swimmer
def swim
p "swim!"
end
end
class Dolphin < Mammal
include Swimmer
def swim
super
p "cute!"
end
end
class Shark < Fish
include Swimmer
def swim
super
p "scary!"
end
end
Dolphin.new.swim
# => "swim!"
# => "cute!"
Shark.new.swim
# => "swim!"
# => "scary!"
include
を使って親クラスとしてSwimmerモジュールを挿入しているので、
オーバーライドして動作を拡張して利用できる。
もちろんオーバーライドせずそのまま利用することもできる。
(@scivolaさんにextend
の活用例を教えて頂いたので、以下追記)
モジュールはインスタンス化できないが、
以下のようにextend
をObject
クラスのインスタンスに使うことで、それっぽいことができる。
module ModuleA
def hoge
p "hoge"
end
end
obj = Object.new
obj.extend ModuleA
obj.hoge
モジュールでsuper!?
保守性、可読性の観点で疑問は残るが、mixinはやろうと思えば色々な使い方ができてしまう。
コードを読めるようになるために慣れる必要がありそう。
module内でインスタンス変数を使ってしまう例
module Fuga
def introduce
p "My name is #{@name}"
end
end
class Hoge
include Fuga
def initialize(name)
@name = name
end
end
Hoge.new("Alice").introduce
# => "My name is Alice"
mixin先に特定のインスタンス変数があることを前提としてしまっている。
個人的には良くないと思う。
以下はmodule内のsuperのヤバイ例
class Poo
def hoge
p "poo"
end
end
module Foo
def hoge
super
p "foo"
end
end
module Fuga
include Foo
def hoge
super
p "fuga"
end
end
class Hoge < Poo
include Fuga
def hoge
super
p "hoge"
end
end
Hoge.new.hoge
# => "poo"
# => "foo"
# => "fuga"
# => "hoge"
最終的に継承関係がHoge -> Fuga -> Foo -> Poo
のようになる。
includeしたモジュールのsuperは、元々の親クラスを呼び出しにくわけだが、
モジュールがモジュールをincludeしていることもありえて、こうなるとかなり厄介。
その他細かいポイント
%記法
以下の記事が参考になりました
インターポレーション
name = "Alice"
p "Hello #{name}, how are you?"
# => "Hello Alice, how are you?"
シンボルと文字列
:
で始めるとシンボルになり、次の特徴を持つ。
- シンボルは、任意の文字列と1対1対応する
- 同じシンボルは必ず同じオブジェクト
- 中身は整数
文字列というよりはEnumに近い。
その性質上ハッシュのキーによく用いられる。
文字列とシンボルの変換
:hoge.to_s
# => "hoge"
"hoge".to_sym
# => :hoge
ハッシュ
普通のハッシュ。
h = {"key1" => "hoge", "key2" => "fuga"}
f["key3"] = "foo"
シンボルがキーの場合は特別な記法がある。
h = {key1: "hoge", key2: "fuga"}
h[:key3] = "foo"
h[:key1]
# => "hoge"
h["key1"]
# => nil
nil関連
リストで存在しないインデックスを指定した時や、
ハッシュで存在しないキーを指定した時は、
例外は発生せず、nilを返す。
対話型の起動
irb
コマンド
終わりに
思ったよりも大変だったが、面白い言語だと思った。
慣れてしまえばPython
と同じくらい便利に使えそう。
map
とかselect
といった集合処理系が強そうなのがとても良い。
Python
も見習ってほしい(リスト内包表記使いにくい)。
一方でRuby
のyield
の仕様は気になった。
Python
のようにジェネレータを返せる方が遅延評価とか便利なんじゃないだろうか。