This page looks best with JavaScript enabled
⚠️

【Ruby】Ruby Silver 3.1 の勉強ついでに覚えたことメモ

 ·  ☕ 8 分で読めます
✏️

はじめに

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は、配列同士の長さが同じことを前提として、縦横逆の配列に組み替える。配列同士の長さが違う場合はエラーが出る。

abの配列の長さが同じ場合は、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
Share on

END
END
@aiandrox

 
目次