目的
実装やレビューにおいて困っている事は特に無いが、より良い実装にするためRubySilverに触れることで更に理解を深め、発想の引き出しを増やしたい。
教材
- https://rex.libertyfish.co.jp/
- https://gist.github.com/sean2121/945035ef2341f0c39bf40762cd8531e0
- [改訂2版]Ruby技術者認定試験合格教本(Silver/Gold対応)
組み込み定数
組み込み定数 | 値 |
---|---|
STDIN | 標準入力 |
STDOUT | 標準出力 |
STDERR | 標準エラー出力 |
DATA | スクリプトの __END__ プログラム以降にアクセスする |
ENV | 環境変数 |
ARGF | スクリプトに指定した引数をファイル名とみなし、それらのファイルを連結した1つの仮想ファイルを表す |
ARGV | スクリプトに与えられた引数を表す配列 |
RUBY_VERSION | Rubyのバージョン |
RUBY_RELEASE_DATE | Rubyのリリース日 |
RUBY_PLATFORM | プラットフォーム |
組み込み変数
予約語一覧
BEGIN class ensure nil self when
END def false not super while
alias defined? for or then yield
and do if redo true __LINE__
begin else in rescue undef __FILE__
break elsif module retry unless __ENCODING__
case end next return until
予約語はクラス名、変数名などに用いることはできません。ただし接頭辞$, @、@@が先頭についたものは予約語とは見なされません。
また、def のあとやメソッド呼び出しのピリオドのあとなどメソッド名であるとはっきり分かる場所ではメソッド名として用いることができます。
ローカル変数とメソッド名が同じ場合の挙動
hoge = 0
def hoge
5 * 100
end
p hoge
0
変数の定義が優先される
現場ではまずやらない書き方だけど理解しておくことが大切。
シンボルと文字列
Rubyの内部実装では、メソッド名や変数名、定数名、クラス名などの`名前'を整数で管理しています。これは名前を直接文字列として処理するよりも速度面で有利だからです。そしてその整数をRubyのコード上で表現したものがシンボルです。
シンボルは、ソース上では文字列のように見え、内部では整数として扱われる、両者を仲立ちするような存在です。
名前を管理するという役割上、シンボルと文字列は一対一に対応します。また、文字列と違い、immutable (変更不可)であり、同値ならば必ず同一です。
p str1 = "aaa".object_id #=> 60
p str2 = "aaa".object_id #=> 80
p sym1 = :aaa.object_id #=> 1082908
p sym2 = :aaa.object_id #=> 1082908
シンボルは内部では整数として扱われるため、文字列よりも処理が速い。
よって、単にラベルとして扱いたい場合には文字列よりもシンボルの方が効率が良い。
1行で書く
本来改行すべきところに;
(セミコロン)を書く。
クラス
class OriginalError < StandardError; end # ここ
begin
# raise StandardError
raise OriginalError
rescue OriginalError => e
puts "1: #{e.class}"
rescue => e
puts "2: #{e.class}"
end
#=> 1: OriginalError
メソッド
def add(num1, num2); p num1 + num2; end
add(1, 2) #=> 3
条件分岐
a = 2
if a == 1; p 'a'; elsif a == 2; p 'b'; else; p 'c'; end #=> "b"
多重代入
例
foo, bar = [1, 2] #=> foo = 1; bar = 2
foo, bar = 1, 2 #=> foo = 1; bar = 2
foo, bar = 1 #=> foo = 1; bar = nil
foo, bar, baz = 1, 2 #=> foo = 1; bar = 2; baz = nil
foo, bar = 1, 2, 3 #=> foo = 1; bar = 2
foo = 1, 2, 3 #=> foo = [1, 2, 3]
*foo = 1, 2, 3 #=> foo = [1, 2, 3]
foo,*bar = 1, 2, 3 #=> foo = 1; bar = [2, 3]
一部を括弧で囲う場合
(a, b), c = 1, 2, 3
p a #=> 1
p b #=> nil
p c #=> 2
上記のように行うと 「(a, b)
」 と 「c
」 に対してそれぞれの値を代入を行う。
引数
可変長引数
第一引数が可変長
def hoge(*n1, n2)
p n1
p n2
end
hoge 1, 2, 3, 4
[1, 2, 3]
4
第二引数が可変長
def hoge(n1, *n2)
p n1
p n2
end
hoge 1, 2, 3, 4
1
[2, 3, 4]
複数の引数を可変長にするとsyntax errorとなる
例)
hoge(n1, *n2, *n3)
hoge(*n1, n2, *n3)
hoge(*n1, *n2, *n3)
#=> syntax error, unexpected `end', expecting end-of-input
キーワード引数
def foo(a:, b:, c:400)
p a + b + c
end
foo(a: 100, b: 200, c: 300) #=> 600
foo(a: 100, b: 200) #=> 700
foo(a: 100) #=> sample.rb:1:in `foo': missing keyword: :b (ArgumentError)
foo(a: 100, b: 200, d: 300) #=> sample.rb:1:in `foo': unknown keyword: :d (ArgumentError)
デフォルト値がないキーワード引数を省略したり、
存在しないキーワード引数を指定すると、ArgumentErrorになる
オプション引数
Rubyにおけるハッシュのように自由にキーワードと値のペアで引数を記述し、それを引き渡せる機能です。そして引き渡した引数はメソッドの内部でハッシュのように扱えます。
def sample(**args)
p args
args.each do |key, value|
puts "key: #{key}, value: #{value}"
end
end
sample(a: 100, b: 200, c: 300)
#=> {:a=>100, :b=>200, :c=>300}
# key: a, value: 100
# key: b, value: 200
# key: c, value: 300
演算子式
==, eql? equal?(オブジェクトの同値性と同一性)
- https://docs.ruby-lang.org/ja/latest/method/Object/i/eql=3f.html
- https://docs.ruby-lang.org/ja/latest/method/Object/i/equal=3f.html
x = 1
y = 1.0
a1 = "aaa"
a2 = 'aaa'
irb(main):005:0> x == y
=> true
irb(main):006:0> x.eql? y
=> false
irb(main):007:0> x.equal? y
=> false
irb(main):008:0> x.equal?(1)
=> true
irb(main):009:0> a1 == a2
=> true
irb(main):010:0> a1.eql? a2
=> true
irb(main):011:0> a1.equal? a2
=> false
-
==
はオブジェクトの内容が等しいか -
eql?
はhashのkeyとして等しいか -
equal?
はobject_idが等しいか
===
- https://docs.ruby-lang.org/ja/latest/doc/symref.html#eq
- https://docs.ruby-lang.org/ja/latest/method/Object/i/=3d=3d=3d.html
- https://docs.ruby-lang.org/ja/latest/method/Range/i/=3d=3d=3d.html
そのクラスかどうか
p String === "ruby" #=> true
p String === 1 #=> false
p Integer === 1 #=> true
p Integer === "ruby" #=> false
範囲に含まれるか
p (1..10) === 10 #=> true
p (1...10) === 10 #=> false
p ('a'..'z') === 'z' #=> true
p ('a'...'z') === 'z' #=> false
正規表現にmatchするか
p /[0-9]{3}/ === "sample987" #=> true
p /[0-9]{3}/ === "sample" #=> false
<=>
(UFO演算子)
p 1000 <=> 999 #=> 1
p 1000 <=> 1000 #=> 0
p 999 <=> 1000 #=> -1
左辺 と 右辺 を比較し、左辺 が大きい時に1、等しい時に 0、小さい時に-1、比較できない時に nil を返す
StringやArrayにもある
- https://docs.ruby-lang.org/ja/latest/method/String/i/=3c=3d=3e.html
- https://docs.ruby-lang.org/ja/latest/method/Array/i/=3c=3d=3e.html
&&
左辺を評価し、結果が偽であった場合はその値(つまり nil か false) を返します。左辺の評価結果が真であった場合には右辺を評価しその結果を返します。 and は同じ働きをする優先順位の低い演算子です。
and
との優先度の違い
p 'a' && 'b' #=> "b"
p 'a' and 'b' #=> "a"
a1 = [1,2,3]
a2 = [4,2,3]
p a1 && a2 #=> [4, 2, 3]
p a1 and a2 #=> [1, 2, 3]
「nilまたはfalseであれば偽、それ以外はすべて真」として扱われる。
また、and
は&&
に比べ優先順位が低い。
どちらかがnilやfalseの場合
a1 = nil
a2 = false
a3 = [1, 2, 3]
p a1 && a3 # 左辺がnil
p a3 && a1 # 右辺がnil
p a2 && a3 # 左辺がfalse
p a3 && a2 # 右辺がfalse
nil
nil
false
false
比較対象がnilとfalseの場合は、左辺を返す
p (nil && false) #=> nil
p (false && nil) #=> false
||
左辺を評価し、結果が真であった場合にはその値を返します。左辺の評価結果が偽であった場合には右辺を評価しその評価結果を返します。 or は同じ働きをする優先順位の低い演算子です。
両辺nilやfalseではない場合
a1 = [1,2,3]
a2 = [4,2,3]
p a1 || a2 #=> [1, 2, 3]
比較対象がnilとfalseの場合は、右辺を返す
p (nil || false) #=> false
p (false || nil) #=> nil
or
との優先度の違い
p false || true #=> true
p false or true #=> false
クラス変数
class Super
@@foo = 0
def foo
@@foo
end
def foo=(value)
@@foo = value
end
end
class Sub < Super
@@foo = 0
def foo
@@foo
end
def foo=(value)
@@foo = value
end
end
super1 = Super.new
super1.foo += 100
super2 = Super.new
super2.foo += 200
sub1 = Sub.new
sub1.foo += 50
puts "#{super1.foo}/#{super2.foo}/#{sub1.foo}" #=> 350/350/350
class Super
@@i = 0
def self.count
@@i += 1
end
end
class Sub < Super; end
puts Super.count #=> 1
puts Sub.count #=> 2
puts Super.count #=> 3
クラス変数はクラス全体で共有され、クラスメソッド内やスーパークラス・サブクラスでも共有される。
ヒアドキュメント
- https://docs.ruby-lang.org/ja/latest/doc/spec=2fliteral.html#here
- https://style.potepan.com/articles/35488.html
EOD (End of Documentの略)
EOF (End of Fileの略)
EOL (End of Lineの略)
EOM (End of Messageの略)
EOS (End of Stringの略)
EOT (End of Textの略)
text = <<EOF
Ruby,
Silver
EOF
p text #=> " Ruby,\n Silver\n"
終端行をインデントしているためエラー発生
text = <<EOF
Ruby,
Silver
EOF
p text #=> can't find string "EOF" anywhere before EOF
開始ラベルを<<-識別子
のようにする事で、終端行をインデント出来る
text = <<-EOF
Ruby,
Silver
EOF
puts text #=> " Ruby,\n Silver\n"
開始ラベルを<<~識別子
のようにする事で、空白の無い文字列として変数に格納
text = <<~EOF
Ruby,
Silver
EOF
p text #=> #=> "Ruby,\n Silver\n"
Array
複数の要素を持つ配列を生成する様々な方法
# 例1
['ruby'] * 3 # 中身の要素が、すべて同一のオブジェクト
%w(ruby) * 3 # 同一
# 例2
Array.new(3, 'ruby') # 同一
# 例3
Array.new(3).fill('ruby') # 同一
# 例4
Array.new(3) { 'ruby' } # 別々
# 例5
'rubyrubyruby'.scan('ruby') # 別々
# 例6
[] << 'ruby' << 'ruby' << 'ruby' # 別々
["ruby", "ruby", "ruby"]
定義の仕方によって、オブジェクトの中身が異なるため注意。
下記はArray.new(3, '初期値')
とArray.new(3) { '初期値' }
の違い。
arr1 = Array.new(3, 'ruby')
arr1.each { |el| p el.object_id }
#=> 60
# 60
# 60
arr1[0].replace('RUBY')
p arr1 #=> ["RUBY", "RUBY", "RUBY"]
arr2 = Array.new(3) { 'ruby' }
arr2.each { |el| p el.object_id }
#=> 80
# 100
# 120
arr2[0].replace('RUBY')
p arr2 #=> ["RUBY", "ruby", "ruby"]
-
Array.new(3, '初期値')
は要素が全て同一のオブジェクトを生成する -
Array.new(3) { '初期値' }
は要素が全て別々のオブジェクトを生成する
文字列にアスタリスクをつけるだけでもArrayを作成出来る
*"a" #=> ["a"]
一つの変数に多重代入した場合
obj = 'a', 'b'
p obj #=> ["a", "b"]
連番の要素を持つ配列を生成する様々な方法
# 例1
(1..10).to_a
# 例2
[*1..10]
# 例3
Array(1..10)
# 例4
(1..10).map { |i| i }
# 例5
(1..10).collect { |i| i }
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- 文字列でも同様。
- 例)
[*'a'..'z']
- 例)
#map
と#collect
は同じ動作をする
要素の追加(いきなりn番目の要素を指定)
arr = [1]
arr[4] = 100
irb(main):001:0> arr = [1]
=> [1]
irb(main):002:0> arr[4] = 100
=> 100
irb(main):003:0> arr
=> [1, nil, nil, nil, 100]
指定した要素分を参照・代入する
参照
arr = ['a', 'b', 'c', 'd', 'e']
p arr[1, 3] #=> ["b", "c", "d"]
インデックス1から、3つ分の要素を取得。
代入(指定範囲内)
arr = ['a', 'b', 'c', 'd', 'e']
arr[1, 3] = ['z'] # 'z' でも同じ
p arr #=> ["a", "z", "e"]
arr = ['a', 'b', 'c', 'd', 'e']
arr[1, 3] = 'x', 'y', 'z' # ['x', 'y', 'z'] でも同じ
p arr #=> ["a", "x", "y", "z", "e"]
指定数よりも過小 or 同じ場合は、代入した値に置き換わるイメージ。
代入(指定した範囲を超えた場合)
arr = ['a', 'b', 'c', 'd', 'e']
arr[1, 2] = 'x', 'y', 'z'
p arr #=> ["a", "x", "y", "z", "d", "e"]
指定数よりも余剰の場合は、全て代入された上で、元の要素が後続にずれる。
集合(Array)
a1 = [100, 100, 100, 200, 300]
a2 = [400, 100, 300]
puts "和集合: #{a1 | a2}"
puts "積集合: #{a1 & a2}"
puts "差集合: #{a1 - a2}"
和集合: [100, 200, 300, 400]
積集合: [100, 300]
差集合: [200]
Array#sort
配列の内容をソートします。要素同士の比較は <=> 演算子を使って行います。sort はソートされた配列を生成して返します。 sort! は self を破壊的にソートし、self を返します。
(省略)
ブロックは第1引数が大きいなら正の整数、両者が等しいなら0、そして第1引数の方が小さいなら負の整数を返さなければいけません。両者を比較できない時は nil を返します。
ary = [7, 2, 9, 1, 4, 3, 6, 8, 0, 5]
ary.sort!{ |a, b| b <=> a } # 降順に並べ替え
ary #=> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
ary.reverse!
ary #=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
<=>
演算子で比較できない要素があると、ArgumentErrorが発生する
['a', 'b', 'c', 1, 2, 3].sort #=> comparison of String with 1 failed (ArgumentError)
Array#zip
自身と引数に渡した配列の各要素からなる配列の配列を生成して返します。生成される配列の要素数は self の要素数と同じです。
ブロック付きで呼び出した場合は、 self と引数に渡した配列の各要素を順番にブロックに渡します。
p [1,2,3].zip([4,5,6], [7,8,9])
# => [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
p [1,2].zip([:a,:b,:c], [:A,:B,:C,:D])
# => [[1, :a, :A], [2, :b, :B]]
p [1,2,3,4,5].zip([:a,:b,:c], [:A,:B,:C,:D])
# => [[1, :a, :A], [2, :b, :B],
# [3, :c, :C], [4, nil, :D], [5, nil, nil]]
p [1,2,3].zip([4,5,6], [7,8,9]) { |ary| p ary }
# => [1, 4, 7]
# [2, 5, 8]
# [3, 6, 9]
# nil
Array#product
レシーバの配列と引数で与えられた配列(複数可)のそれぞれから要素を1個ずつとって配列とし,それらのすべての配列を要素とする配列を返します。
返される配列の長さは,レシーバと引数で与えられた配列の長さのすべての積になります。
[1, 2].product([3, 4]) #=> [[1, 3], [1, 4], [2, 3], [2, 4]]
Array#transpose
自身を行列と見立てて、行列の転置(行と列の入れ換え)を行います。
転置した配列を生成して返します。
空の配列に対しては空の配列を生成して返します。
[[1, 3], [1, 4], [2, 3], [2, 4]].transpose #=> [[1, 1, 2, 2], [3, 4, 3, 4]]
Array#slice
指定された自身の部分配列を返します。
p [1, 2, 3, 4].slice(2, 1) #=> [2]
p [0, 1, 2].slice(0, 2) #=> [0, 1]
p [0, 1, 2].slice(2..3) #=> [2]
p [0, 1, 2].slice(10, 1) #=> nil
Array#join
[1, 2, 3].join('-') #=> "1-2-3"
*
でも同じことが可能
[1, 2, 3] * '-' #=> "1-2-3"
Array#shift
配列の先頭の要素を取り除いてそれを返します。引数を指定した場合はその個数だけ取り除き、それを配列で返します。
空配列の場合、n が指定されていない場合は nil を、指定されている場合は空配列を返します。
また、n が自身の要素数より少ない場合はその要素数の配列を返します。どちらの場合も自身は空配列となります。
arr = [0, 1, 2, 3, 4]
p arr.shift #=> 0
p arr #=> [1, 2, 3, 4]
p [].shift #=> nil
p [].shift(1) #=> []
破壊的メソッド
Array#prepend
指定された obj を引数の最後から順番に配列の先頭に挿入します。引数を指定しなければ何もしません。
arr = [1,2,3]
arr.unshift
p arr #=> [1, 2, 3]
arr.unshift 0
p arr #=> [0, 1, 2, 3]
arr.unshift [0]
p arr #=> [[0], 0, 1, 2, 3]
arr.unshift 1, 2
p arr #=> [1, 2, [0], 0, 1, 2, 3]
破壊的メソッド
Array#pop
自身の末尾から要素を取り除いてそれを返します。引数を指定した場合はその個数だけ取り除き、それを配列で返します。
arr = [1, [2, 3], 4]
p arr.pop # => 4
p arr.pop # => [2, 3]
p arr # => [1]
p arr.pop # => 1
p arr.pop # => nil
p arr # => []
arr = [1, 2, 3]
p arr.pop(2) #=> [2, 3]
p arr #=> [1]
破壊的メソッド
Array#each_index
各要素のインデックスに対してブロックを評価します。
[*'a'..'c'].each_index { |i| p i }
# => 0
# 1
# 2
Array#clear
配列の要素をすべて削除して空にします。
ary = ['a', 'b']
ary.clear
p ary #=> []
破壊的メソッド
Hash
Hashの定義
key = Class.new
value = 10000
# 例1
hash = {key => value}
# 例2
hash = Hash.new
hash[key] = value
# 例3
hash = Hash[key, value]
# 例4
hash = {}
hash.store(key, value)
キーが重複している場合
hash = { a: 100, b: 200, c: 300, a: 10, c: 20 }
irb(main):001:0> { a: 100, b: 200, c: 300, a: 10, c: 20 }
(irb):1: warning: key :a is duplicated and overwritten on line 1
(irb):1: warning: key :c is duplicated and overwritten on line 1
=> {:b=>200, :a=>10, :c=>20}
警告が出る。後ろのバリューに上書きされる。
Hashのvalueを複数取得する
hash = { apple: 100, grape: 300 }
p hash.values_at(:apple, :grape) #=> [100, 300]
HashをArrayにする
{a: 100, b: 200}.to_a #=> [[:a, 100], [:b, 200]]
キーが存在するかどうか調べる
hash = { apple: 100, grape: 300 }
p hash.has_key?(:apple) #=> true
p hash.key?(:apple) #=> true
p hash.include?(:apple) #=> true
p hash.member?(:apple) #=> true
メソッドの引数にHashを渡す
def sample(a, b, c)
p c
end
sample 'a', 'b', {c: 'c', d: 'd', e: 'e'} #=> {:c=>"c", :d=>"d", :e=>"e"}
sample 'a', 'b', c: 'c', d: 'd', e: 'e' #=> {:c=>"c", :d=>"d", :e=>"e"}
波括弧を省略した場合も、Hashとして渡される
Hash#invert
値からキーへのハッシュを作成して返します。
異なるキーに対して等しい値が登録されている場合、最後に定義されている値が使用されます。
h = { "a" => 0, "b" => 100, "c" => 200, "d" => 300, "e" => 300 }
p h.invert #=> {0=>"a", 100=>"b", 200=>"c", 300=>"e"}
h = {a: 100, b: 100}
p h.invert #=> {100=>:b}
Hash#clear
ハッシュの中身を空にします。
空にした後のselfを返します。デフォルト値の設定はクリアされません。
h = {apple: 100, grape: 300}
h.clear
p h #=> {}
破壊的メソッド
Enumerable
Enumerable#partition
a, = (1..5).partition(&:odd?)
p a #=> [1, 3, 5]
irb(main):001:0> (1..5).partition(&:odd?)
=> [[1, 3, 5], [2, 4]]
a =
はともかくa, =
っていう書き方でもエラーなく出来るんだな・・。
Enumerable#each_cons
要素を重複ありで n 要素ずつに区切り、ブロックに渡して繰り返します。
ブロックを省略した場合は重複ありで n 要素ずつ繰り返す Enumerator を返します。
(1..10).each_cons(3){ |v| p v }
# => [1, 2, 3]
# [2, 3, 4]
# [3, 4, 5]
# [4, 5, 6]
# [5, 6, 7]
# [6, 7, 8]
# [7, 8, 9]
# [8, 9, 10]
実務ではまだ見かけたことないけど競プロとかで使えそう。
Numeric
Numeric#abs2
自身の絶対値の 2 乗を返します。
p 5.abs2 # => 25
p -5.abs2 # => 25
p 5.0.abs2 # => 25.0
p -5.0.abs2 # => 25.0
Numeric#step
self からはじめ step を足しながら limit を越える前までブロックを繰り返します。
step は負の数も指定できます。また、limit や step には Float なども指定できます。
irb(main):001:0> 2.step(5){|n| p n}
2
3
4
5
=> 2
irb(main):002:0> 1.1.step(1.5, 0.1) {|n| p n}
1.1
1.2000000000000002
1.3
1.4000000000000001
1.5
=> 1.1
irb(main):003:0> 10.step(6, -1){|n| p n}
10
9
8
7
6
=> 10
irb(main):004:0> 3.step(by:2, to:10){|n| p n}
3
5
7
9
=> 3
irb(main):005:0> 1.step(5,1){|n| p n}
1
2
3
4
5
=> 1
浮動小数点数の 0.1 は 2進数では正確な表現ができない
(2進数で 0.1は 0.00011001100....となる)
String
String#delete
delete(*strs) -> String
self から strs に含まれる文字を取り除いた文字列を生成して返します。
p 'Ruby on Rails'.delete('Rails') #=> "uby on "
p '0123456789'.delete('0-48-') #=> "5679"
delete('0-48-')
は、'0-4'
で0〜4までの数字を取り除き、'8-'
では範囲指定とは見なされず、8
と-
を削除している。
String#split
str1 = '10;20:30;40'
p str1.split(';|:') #=> ["10;20:30;40"]
p str1.split(/;|:/) #=> ["10", "20", "30", "40"]
str2 = 'a,b,c,d'
p str2.split(/,/, 2) #=> ["a", "b,c,d"]
str3 = 'A-B-C'
p str3.split /(-)/ #=> ["A", "-", "B", "-", "C"]
String#strip
p " abc \r\n".strip #=> "abc"
p "abc\n".strip #=> "abc"
p " abc".strip #=> "abc"
p "abc".strip #=> "abc"
p " \0 abc \0".strip # => "\000 abc" # 右側のみ "\0" も取り除く
str = "\tabc\n"
p str.strip #=> "abc"
p str #=> "\tabc\n" (元の文字列は変化しない)
文字列の先頭と末尾の空白文字(\t\r\n\f\v)を取り除く。
String#chomp
p "foo\n".chomp # => "foo"
p "foo\n".chomp("\n") # => "foo"
p "foo\r\n".chomp("\r\n") # => "foo"
$/ = "\n" # デフォルト値と同じ
p "foo\r".chomp # => "foo"
p "foo\r\n".chomp # => "foo"
p "foo\n".chomp # => "foo"
p "foo\n\r".chomp # => "foo\n"
p "string\n".chomp(nil) # => "string\n"
p "foo\r\n\n".chomp("") # => "foo"
p "foo\n\r\n".chomp("") # => "foo"
p "foo\n\r\r".chomp("") # => "foo\n\r\r"
末尾から改行コードを取り除く。
String#chop
irb(main):001:0> str = "Sample \r\n"
=> "Sample \r\n"
irb(main):002:0> str.chop
=> "Sample "
irb(main):003:0> str.chop
=> "Sample "
irb(main):004:0> "Sample".chop
=> "Sampl"
irb(main):005:0> ''.chop
=> ""
末尾の文字を取り除く。空白は取り除かない。
ただし、文字列の末尾が"\r\n"であれば、2文字とも取り除く。
String#to_i
arr = [
"a".to_i(36), # 36進数の "a" を10進数に変換すると 10 になる。
"070".to_i(0), # to_i(0)は文字列を適切な進数として解釈する。結果として、56(8進数の70を10進数に変換した結果)が配列に格納される。
nil.to_i, # nil は整数に変換されると 0 になる。
"0b0001".to_i # "0b0001"は2進数。10進数に変換すると 1 になる。
]
p arr #=> [10, 56, 0, 0]
Time
時刻を表すクラスです。
Time.now は現在の時刻を返します。
t = Time.now + (60 * 60 * 24) # 実行時の日時から24時間後(86400秒後)の日時
p t
Date
書式文字列 | 意味 |
---|---|
%x | 日付(%m/%d/%y) |
%m | 月を表す数字(01-12) |
%M | 分(00-59) |
%d | 日(01-31) |
%D | 日付(%m/%d/%y) |
%y | 西暦の下2桁(00-99) |
%Y | 西暦を表す数(9999) |
require 'date'
d1 = Date.new(2024, 12, 25)
p d1.strftime('%x') #=> "12/25/24"
p d1.strftime('%D') #=> "12/25/24"
p d1.strftime('%m/%d/%Y') #=> "12/25/2024"
p d1.strftime('%M/%d/%y') #=> "00/25/24"
d2 = Date.parse('2024-12-25')
p d2 += 10 #=> #<Date: 2025-01-04 ((2460680j,0s,0n),+0s,2299161j)>
p d2.year #=> 2025
p d2.strftime('%a') #=> "Sat"
Dir
ディレクトリの操作を行うためのクラスです。
Dir.chdir('/Users/sample/Desktop/Ruby')
p Dir.pwd # "/Users/sample/Desktop/Ruby"
Thread
スレッドを表すクラスです。スレッドとはメモリ空間を共有して同時に実行される制御の流れです。 Thread を使うことで並行プログラミングが可能になります。
Threadクラスのオブジェクトを生成するメソッド
Thread.new
Thread.fork
Thread.start
File
- https://docs.ruby-lang.org/ja/latest/class/File.html
- https://docs.ruby-lang.org/ja/latest/method/Kernel/m/open.html
- https://docs.ruby-lang.org/ja/latest/method/IO/i/seek.html
- https://docs.ruby-lang.org/ja/latest/method/IO/i/rewind.html
- https://style.potepan.com/articles/27826.html
r
: 読み込み
r+
: 読み書き 書き込み時はファイル文頭から上書き
w
: 新規書き込み(ファイルが既に存在していればその内容を空に。)
w+
: 読み書き 書き込み時は既存の内容を削除して新規作成
a
: 追記書き込み
a+
: 読み書き 書き込み時はファイル末尾に追記
'a'
File.open('sample.txt', 'a') do |f|
f.write("waiwai 1\n")
f.write("waiwai 2\n")
end
実行:ruby sample.rb
waiwai 1
waiwai 2
# 改行
もう一度実行:ruby sample.rb
waiwai 1
waiwai 2
waiwai 1
waiwai 2
# 改行
'w'
File.open('sample.txt', 'w') do |f|
f.write("waiwai 1\n")
f.seek(0, IO::SEEK_SET) # ファイルポインタを0の位置まで移動する
f.write("waiwai 2\n")
end
実行:ruby sample.rb
waiwai 2
# 改行
もう一度実行:ruby sample.rb
waiwai 2
# 改行
ファイルポインタの位置を変えて実行
File.open('sample.txt', 'w') do |f|
f.write("waiwai 1\n")
f.seek(3, IO::SEEK_SET) # ファイルポインタを3の位置まで移動する
f.write("waiwai 2\n")
end
実行:ruby sample.rb
waiwaiwai 2
# 改行
r+
recode 1
recode 2
recode 3
open('sample.txt', 'r+') do |f|
data = f.read.upcase
f.rewind # ファイルポインタを先頭に移動
f.puts data
end
RECODE 1
RECODE 2
RECODE 3
# 改行
'a+'
recode 1
recode 2
recode 3
open('sample.txt', 'a+') do |f|
data = f.read.upcase
f.rewind # ファイルポインタを先頭に移動
f.puts data
end
recode 1
recode 2
recode 3RECODE 1
RECODE 2
RECODE 3
# 改行
w+
recode 1
recode 2
recode 3
open('sample.txt', 'w+') do |f|
data = f.read.upcase
f.rewind # ファイルポインタを先頭に移動
f.puts data
end
# 改行1
# 改行2
join
File.join('sample', 'ruby', 'silver') #=> "sample/ruby/silver"
問題例
※ 実行結果を問う問題(実際は選択式)
例1
p "ruby" * 3 **2
"rubyrubyrubyrubyrubyrubyrubyrubyruby"
3の2乗分増えている。
例2
a = [100, 200, 300, 500, 700]
b = [100, 300, 400, 700, 800]
c = false || true ? true && false ? a | b : a & b : b ;
p c
[100, 300, 700]
-
false || true
は true -
true && false
は false - よって、
a & b
(積集合)の値が表示される - シンプルなイメージにすると以下
-
c = false || true ? 三項演算子 : b ;
- 三項演算子の中で、もう一度三項演算子を使っている
-
例3
10.times{ |i| print i == 2..i == 5 ? 'T' : 'F' }
FFTTTTFFFF
-
i == 2..i == 5
という書き方が出来る
例4
arr = []
arr << 10 && false
true || arr << 20
false && arr << 30
false || arr << 40
p arr
[10, 40]
-
&&
演算子:左辺が真と評価された場合のみ、右辺も評価される。 -
||
演算子:左辺が偽と評価された場合のみ、右辺が評価される。
例5
p [1, 2, 3].inject{|x, y| x + y ** 2} rescue p $!
p [1, 2, 3].inject(0){|x, y| x + y ** 2} rescue p $!
p [1, 2, 3].inject([]){|x, y| x << y ** 2} rescue p $!
p [1, 2, 3].inject {|x, y| x + y ** 2} rescue p $!
p [1, 2, 3].inject do|x, y| x + y ** 2 end rescue p $!
14
14
[1, 4, 9]
14
#<LocalJumpError: no block given>
do ... end
と{ ... }
を比べた場合、{ ... }
の方が結合度が強い
#injectでdo...end
を使用する場合は、以下のようにする
result = [1, 2, 3].inject(0) do |x, y|
x + y ** 2
end
p result #=> 14
Enumerable#inject
リストのたたみこみ演算を行います。
numbers = [1,90,40,39,29,10,30,50,69]
num = numbers.inject do |i, j|
i > j ? i : j # 2つの整数値を比較し、より大きな値を取り出しそれをブロックに渡す
end
p num #=> 90
前回のブロックの戻り値をブロックに渡して繰り返し実行する
$!
とは
最後に例外が発生したときの Exception オブジェクトです。該当する例外がないときは nil です。
Kernel.#raise によって設定されます。
この変数はスレッドローカル、読み取り専用です。
例6
def hoge(step = 1)
current = 0
Proc.new {
current += step
}
end
p1 = hoge
p2 = hoge(5)
p1.call
p1.call
p1.call
p2.call
p2.call
p p2.call
15
Proc
Proc がローカル変数のスコープを保持している
- 今回の場合、
#hoge
は一度しか呼ばれていない- よって、ローカル変数
current
の初期化も一回のみ - 途中で
p2 = hoge(3)
等として#hoge
を呼び出せばcurrent
は初期化される
- よって、ローカル変数
- Procはローカル変数のスコープを保持している
-
p2.call
が呼ばれる度に5ずつインクリメントされる -
p1
はp1
で値を保持している
-
例7
class SuperCalc
attr_reader :s
def initialize(x, y)
@s = x * y
end
end
class SubCalc < SuperCalc
attr_reader :v
def initialize(x, y, z)
super(x, y)
@v = x * y * z
end
end
obj = SubCalc.new(2,5,7)
puts "#{obj.s}, #{obj.v}"
10, 70
-
super
を使えば、親クラスの同名のメソッドを呼び出すことが可能。 -
super
には通常のメソッドと同じように引数も指定可能。
例8
p "Ruby Silver".class
p String.superclass
class Hoge; end
class Foo < Hoge; end
p Hoge.superclass
p Foo.superclass
String
Object
Object
Hoge
- https://docs.ruby-lang.org/ja/latest/method/Object/i/class.html
- https://docs.ruby-lang.org/ja/latest/method/Class/i/superclass.html
#class
はオブジェクトのクラス、#superclass
はそのクラスのスーパークラスを返す。明示的にスーパークラスを指定しない場合は、Objectがスーパークラスとなる。
例9
foo = ['a', 'b', 'c']
bar = foo
baz = foo.dup
bar[3] = 'd'
p foo
p bar
p baz
["a", "b", "c", "d"]
["a", "b", "c", "d"]
["a", "b", "c"]
変数 foo
と bar
は同じ配列オブジェクトを参照している。
p foo.object_id #=> 60
p bar.object_id #=> 60
p baz.object_id #=> 80
例10
class Object
def food
p 'rice'
end
end
['a', 'b'].food
{a: 1, b: 2}.food
"rice"
"rice"
Arrayクラス、Hashクラス共にObjectクラスを継承している。
Rubyでは組み込みクラスを含め、定義済みのクラスに対してメソッド追加・既存メソッドの書き換えが可能。
例11
print "waiwai\n" unless false || nil
waiwai
unless false || nil
の条件は、if !false && !nil
と同じ意味(ドモルガンの法則)