はじめに
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 ): 02 8 : 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 ) > 0 b20
( 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