はじめに
1月はほとんど有給消化期間だったので、せっかくなのでRuby Silverの申込みをしました。
もう少し早め(1/20まで)に申し込みをしておけば、1度落ちても再受験無料キャンペーンをしていたのですが、残念ながら少し遅かったです。
試験対策
元々業務でRubyは使っていましたが、あまりメソッドの細かい仕様は覚えていませんでした。
教本は読まず、演習問題をやりながら公式のドキュメントを見たり、挙動をirbやpryで確認していました。
RExは8周しました(44→52→74→88→84→86→80→92)
その後、prep-test/silver_ja.md at version3をやったら70点くらいで絶望しました。
試験はギリギリだったので、RExは安定して90点以上取れるくらいになるくらい回したほうがよさそうです……。
結果
76/100で、ギリギリ合格でした。
配列操作系のメソッドがうろ覚えすぎでした……。
学んだこと
ざっくりまとめているので、省略している仕様もあります。
一致
eql?
と==
は大体同じ。
equal?
はオブジェクトIDも同じである必要がある。
TrueClass
, FalseClass
, NilClass
, Symbol
, Integer
クラスのインスタンスはオブジェクトIDが同じになる。
ヒアドキュメント
開始位置の識別子をダブルクオーテーションで囲む or クオーテーションなしの場合は式展開できる。
1
2
3
4
|
hoge = 1
s = <<EOS
hoge #{hoge}
EOS
|
<<-
の場合は、終了位置の識別子をインデントすることができる。逆に言うと、<<
の場合は、行頭に終了位置の識別子がないといけない。
<<~
の場合は、終了位置の識別子をインデントすることができるのに加えて、テキスト内のインデントを一番左のものに合わせることができる。
1
2
3
4
5
|
irb(main):026:0" s = <<-EOS
irb(main):027:0" hogehoge
irb(main):028:0" foo
irb(main):029:0> EOS
=> " hogehoge\n foo\n"
|
1
2
3
4
5
|
irb(main):030:0" s = <<~EOS
irb(main):031:0" hogehoge
irb(main):032:0" foo
irb(main):033:0> EOS
=> "hogehoge\n foo\n"
|
ファイルopen第二引数のモード
r
ファイルの先頭から読み込みのみ(デフォルト)
r+
ファイルの先頭から読み書き可能
w
既存のファイルを0バイトに切り詰めるか新しくファイルを作成し、書き込みのみ
w+
既存のファイルを0バイトに切り詰めるか新しくファイルを作成し、読み書き可能
a
存在しなければ新しくファイルを作成し、ファイル末尾に追記。書き込みのみ
a+
存在しなければ新しくファイルを作成し、ファイル末尾に追記。読み書き可能
読み込みのみ:r
書き込みのみ:w
, a
読み書き可能:+
付き
既存のファイルが存在しない場合はエラーになる:r
, r+
追記系・書き込み位置が末尾から:a
, a+
先頭から追記:r+
元のファイルの内容を削除する:w
, w+
配列操作
要素を追加・削除するメソッド
shift
:先頭から取り出す
unshift
:先頭に入れる
pop
:最後から取り出す
push
:最後に入れる
shift
, pop
で、引数がない場合は取り出した値を返して、引数がある場合は配列を返す。
1
2
3
4
5
6
7
8
|
[46] pry(main)> s = [0,1,2,3]
=> [0, 1, 2, 3]
[47] pry(main)> s.shift
=> 0
[48] pry(main)> s = [0,1,2,3]
=> [0, 1, 2, 3]
[49] pry(main)> s.shift(1)
=> [0]
|
unshift
, push
で引数がない場合は何も追加せず、引数がある場合は追加する。
1
2
3
4
5
6
7
8
|
[37] pry(main)> s = ["one", "two", "three"]
=> ["one", "two", "three"]
[38] pry(main)> s.push
=> ["one", "two", "three"]
[39] pry(main)> s.push(1,2)
=> ["one", "two", "three", 1, 2]
[40] pry(main)> s.push([1,2])
=> ["one", "two", "three", 1, 2, [1, 2]]
|
配列から要素を削除するメソッド
slice, slice!
引数にインデックスを指定して、その要素を返す。slice!
は破壊的メソッド、配列からその要素を削除する。
1
2
3
4
5
6
7
8
9
10
|
[1] pry(main)> a = ["a", "b", "c", "a", "b", "c"]
=> ["a", "b", "c", "a", "b", "c"]
[2] pry(main)> a.slice(2)
=> "c"
[3] pry(main)> a
=> ["a", "b", "c", "a", "b", "c"]
[4] pry(main)> a.slice!(2)
=> "c"
[5] pry(main)> a
=> ["a", "b", "a", "b", "c"]
|
slice!
とdelete_at
は、引数がIntegerの場合は同じ挙動をする。
しかし、slice!
の引数にRangeを渡したり、第二引数を渡したりできるが、delete_at
の引数にはIntegerを渡すことしかできない。
1
2
3
4
5
6
7
8
9
|
[1] pry(main)> a = ["a", "b", "c", "a", "b", "c"]
=> ["a", "b", "c", "a", "b", "c"]
[2] pry(main)> a.delete_at(2)
=> "c"
[3] pry(main)> a
=> ["a", "b", "a", "b", "c"]
[4] pry(main)> a.delete_at(2..3)
TypeError: no implicit conversion of Range into Integer
from (pry):5:in `delete_at'
|
delete
破壊的メソッド。引数に指定された値を返し、配列からすべて削除する。
配列に値がない場合はnil
を返す。
1
2
3
4
5
6
7
8
|
[1] pry(main)> a = ["a", "b", "c", "a", "b", "c"]
=> ["a", "b", "c", "a", "b", "c"]
[2] pry(main)> a.delete("b")
=> "b"
[3] pry(main)> a
=> ["a", "c", "a", "c"]
[4] pry(main)> a.delete("z")
=> nil
|
Array#delete (Ruby 3.2 リファレンスマニュアル)
select, filter, find_all ⇔ reject, (delete_if)
引数にはブロックを渡す。
select
, filter
, find_all
は、ブロックの中身が真の要素のみの配列を返す。
reject
はブロックの中身が偽の要素のみの配列を返す。
すべて、!
があった場合は破壊的メソッドになる。reject!
とdelete_if
はエイリアス。
配列と配列を組み合わせるメソッド
zip, transpose
zip
は、配列と配列を組み合わせて新しい配列にする。同じインデックス同士。配列同士の長さが違う場合は、足りない要素をnil
で埋める。
transpose
は、配列同士の長さが同じことを前提として、縦横逆の配列に組み替える。配列同士の長さが違う場合はエラーが出る。
a
とb
の配列の長さが同じ場合は、a.zip(b)
と[a, b].transpose
は同じ挙動になる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
[1] pry(main)> a = ["a", "b", "c"]
=> ["a", "b", "c"]
[2] pry(main)> b = [1, 2]
=> [1, 2]
[3] pry(main)> a.zip(b)
=> [["a", 1], ["b", 2], ["c", nil]]
[4] pry(main)> a.zip # 引数がない場合は、単体で配列がネストされる
=> [["a"], ["b"], ["c"]]
[5] pry(main)> a.zip(b, b) # 引数を複数渡すこともできる
=> [["a", 1, 1], ["b", 2, 2], ["c", nil, nil]]
[6] pry(main)> c = a.zip(b, b)
=> [["a", 1, 1], ["b", 2, 2], ["c", nil, nil]]
[7] pry(main)> c.transpose
=> [["a", "b", "c"], [1, 2, nil], [1, 2, nil]]
[8] pry(main)> [["a", "b", "c"], [1, 2], [1, 2]].transpose
IndexError: element size differs (2 should be 3)
from (pry):7:in `transpose'
|
Array#zip (Ruby 3.2 リファレンスマニュアル)
Array#transpose (Ruby 3.2 リファレンスマニュアル)
product
それぞれの配列同士で、1つずつ要素を取ったすべての組み合わせを返す。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
[1] pry(main)> a = ["a", "b", "c"]
=> ["a", "b", "c"]
[2] pry(main)> b = [1, 2, 3]
=> [1, 2, 3]
[3] pry(main)> a.product(b)
=> [["a", 1], ["a", 2], ["a", 3], ["b", 1], ["b", 2], ["b", 3], ["c", 1], ["c", 2], ["c", 3]]
[5] pry(main)> a.product # 引数がない場合は、単体で配列がネストされる
=> [["a"], ["b"], ["c"]]
[4] pry(main)> a.product(b, b) # 引数を複数渡すこともできる
=> [["a", 1, 1],
["a", 1, 2],
["a", 1, 3],
["a", 2, 1],
... # (省略)
["c", 2, 2],
["c", 2, 3],
["c", 3, 1],
["c", 3, 2],
["c", 3, 3]]
|
配列の要素取得
1
2
3
4
5
6
|
[1] pry(main)> "aiueo"[1, 3] # index1から3要素
=> "iue"
[2] pry(main)> "aiueo"[1..2] # index1から2まで
=> "iu"
[3] pry(main)> "aiueo"[1...2] # index 1から2(を含まない)まで
=> "i"
|
n進数の操作
0b
(2進数)
0
, 0o
(8進数)
0d
(10進数)
0x
(16進数)
ちなみに、その進数ではありえない不正な値の場合、下記のようにエラーになる。
1
2
3
4
5
|
[1] pry(main)> 0b10
=> 2
[2] pry(main)> 0b20
(eval):2: numeric literal without digits
SyntaxError: unexpected integer literal, expecting end-of-input
|
to_i
to_i
は、10進数のIntegerを返す。
to_i
(引数なし)は、先頭から有効な数値を返す。数字以外の文字列がある場合は0
になる。
to_i(2)
は、呼び出し元の文字列が2進数であることを前提とする。
to_i(0)
は、呼び出し元の文字列を進数
引数に指定できるのは、0, 2 〜 36の数値のみ。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[4] pry(main)> "a70".to_i
=> 0
[5] pry(main)> "70aaa".to_i
=> 70
[6] pry(main)> "070".to_i
=> 70
[7] pry(main)> "070".to_i(0)
=> 56
[8] pry(main)> "070".to_i(1)
ArgumentError: invalid radix 1
from (pry):8:in `to_i`
[9] pry(main)> "70".to_i(2)
=> 0
|
似たメソッドにoct
, hex
がある。
1
2
3
4
5
6
7
8
9
10
|
[14] pry(main)> '10'.oct
=> 8
[15] pry(main)> '0x10'.oct
=> 16
[16] pry(main)> '0b10'.oct
=> 2
[17] pry(main)> '10'.hex
=> 16
[18] pry(main)> '0b10'.hex
=> 2832
|
to_s
引数に指定できるのは、2 〜 36の数値のみ。
呼び出し元を(引数)進数の文字列にする。
1
2
3
4
|
p 10.to_s(2) # => "1010"
p 10.to_s(8) # => "12"
p 10.to_s(16) # => "a"
p 35.to_s(36) # => "z"
|
1
2
3
4
5
6
7
8
9
10
|
[1] pry(main)> 10.to_s(0)
ArgumentError: invalid radix 0
from (pry):1:in `to_s`
[2] pry(main)> 10.to_s(1)
ArgumentError: invalid radix 1
from (pry):2:in `to_s`
[3] pry(main)> 0xff.to_s
=> "255"
[4] pry(main)> 0xff.to_s(16)
=> "ff"
|
トップレベルで定義したメソッドって、呼び出せるんですね
1
2
3
4
5
6
7
8
9
10
11
12
|
[1] pry(main)> def kansu
[1] pry(main)* self.to_s
[1] pry(main)* end
=> :kansu
[2] pry(main)> 1.kansu
=> "1"
[3] pry(main)> 'a'.kansu
=> "a"
[4] pry(main)> nil.kansu
=> ""
[5] pry(main)> kansu
=> "main"
|
object main (Ruby 3.2 リファレンスマニュアル)
Dir
, File
, IO
系のクラスメソッド
1
2
3
4
5
6
7
8
9
10
11
|
File.dirname("text.txt")
File.directory?("text.txt")
File.file?("text.txt")
File.chmod()
File.chown()
File.delete(ファイル名)
Dir.delete(ディレクトリ名)
Dir.rmdir(ディレクトリ名)
Dir.home # ホームディレクトリのフルパス
Dir.pwd # カレントディレクトリのフルパス
|
1
2
3
4
5
6
7
|
[1] pry(main)> Dir.rmdir('list.txt')
Errno::ENOTDIR: Not a directory @ dir_s_rmdir - list.txt
from (pry):1:in `rmdir`
[3] pry(main)> Dir.exist?('list.txt')
=> false
[4] pry(main)> File.exist?('list.txt')
=> true
|
定数について
定数はアルファベット大文字で始まらないといけない。2文字目以降は小文字でもいいが、すべて大文字にすることを推奨される。
定数定義をメソッド内で行うことはできない。
定数に再代入をすると、警告文が出るが、再代入自体は可能。
メソッドによる上書きの場合は、値の変更は可能だし、警告文も出ない。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
[1] pry(main)> Ruby = "Ruby"
=> "Ruby"
[2] pry(main)> Ruby = "RUBY"
(pry):2: warning: already initialized constant Ruby
(pry):1: warning: previous definition of Ruby was here
=> "RUBY"
[3] pry(main)> p Ruby
"RUBY"
=> "RUBY"
[4] pry(main)> Ruby.downcase!
=> "ruby"
[5] pry(main)> p Ruby
"ruby"
=> "ruby"
[6] pry(main)> def hoge
[6] pry(main)* HOGE = "hoge"
[6] pry(main)* end
(eval):3: dynamic constant assignment
HOGE = "hoge"
^~~~
|
その他
- 同名の変数とメソッドがあった場合は、変数が優先して呼び出される
- ぼっち演算子でレシーバーが
nil
の場合は、問答無用でnil
を返す
nil.to_i => 0
, nil&.to_i => nil