Closed55
Ruby Silver勉強中のメモ
Ruby勉強で便利なデバック
- Rubyオンリーでアルゴリズムの勉強とかしてる時に、binding.pryみたいの簡単に使えたらいいのに〜と思った時
- プログラムの冒頭に
require 'debug'
を入れて、メソッドの中身確認したいところにdebugger
かbinding.break
と入れると処理が止まる。 -
rdbg
コマンドを使用した方法やVSCodeを使用した方法もあるようです - debugはRuby2.7以降に導入されているデバック用のライブラリ
使用する流れ
-
require 'debug'
とライブラリを読み込んで使用してみました
実際の流れ
require 'debug' #これないとundefined local variable or method `debugger' for an instance of Employee (NameError)って言われる
#...
end
def <=> other
binding.break #ここの中身みたい
return self.id <=> other.id
end
end
employees = []
#..
- この状態で
$ ruby <ファイル名>
で実行すると、下記の通り処理が止まる。
[8, 17] in sj13.rb
8| end
9| def to_s #文字列に変換するためのメソッド
10| return "#{@id}:#{@name}"
11| end
12| def <=> other
=> 13| debugger
14| return self.id <=> other.id
15| end
16|
17| end
=>#0 Employee#<=>(other=#<Employee:0x0000000100c62db0 @id="1", @n...) at sj13.rb:13
#1 [C] Array#sort! at sj13.rb:22
# and 1 frames (use `bt' command for all frames)
(rdbg) p self.id # command #self.id の中身確認している
=> "3" #3か
参考資料
使用した教材
- 合格教本(書籍)
- Rex(ネット・無料)
- Githubの連携必要
- Rex
- 模擬試験問題集
- 難易度低めなので後回しにした
- 模擬試験問題集
- プロを目指す人のためのRuby入門(チェリー本🍒)
- 問題解くのと並行して最初から読みつつ、リファレンス的な使い方も
- プロを目指す人のためのRuby入門[改訂2版] 言語仕様からテスト駆動開発・デバッグ技法まで (Software Design plus) | 伊藤 淳一 |本 | 通販 | Amazon
試験前確認
合格した方のブログ記事
Rubyのデフォルトの文字エンコード
エンコード・文字コード・文字エンコード
- エンコード:データを他の形式に変換すること(参考)
- 文字コード:文字に割り当てられた数字(参考)=対応表📝
- 文字エンコード:文字とコード(数字)の対応表、もしくは、使用する対応表を切り替えること(参考)=対応表をみて変換する人📝👀🗣️
なぜ必要か
- コンピューターは数字しか理解できないので人間が入力した「あ」とか「V」とかを変換する必要があるため
UTF-8
- 文字エンコードの一つ
- 世界的に最も基本的な文字コードであるASCIIの規定をそのまま使用できる
- 文字コードの範囲が広く文字化けをしない
- 参考資料:- UTF-8ってなに?文字コードの仕組みや文字化けの原因・対処法についても解説 | クリエイターのための総合情報サイト CREATIVE VILLAGE
Rubyデフォルトの文字エンコードはUTF-8
- 確認方法
puts "なかじ".encoding.name #=>UTF-8
文字↔️文字コード
文字→文字コード
p "B".ord #=> 66
- 最初の文字コードを返すため、2文字目以降は無視される
- String#ord (Ruby 3.3 リファレンスマニュアル)
文字コード→文字
p 66.chr #=>"B"
- 引数無しで呼ばれた場合は self を US-ASCII、ASCII-8BIT、デフォルト内部エンコーディングの順で優先的に解釈(エラーにはならない)
- self を与えられたエンコーディングで正しく解釈できない場合、RangeErrorが発生
- Integer#chr (Ruby 3.3 リファレンスマニュアル)
ハッシュでsortメソッドを使用する際の注意点
前提
- HashクラスはEnumerableから継承しているためsortメソッドが使用可能です
- class Hash (Ruby 3.3 リファレンスマニュアル)
- Enumerable#sort (Ruby 3.3 リファレンスマニュアル)
戻り値が二次元配列になる
- nasu,ichigo,katsuoをアルファベット順に並び替えます
a = {"Yasai" => "nasu", "Kudamono"=>"ichigo", "Sakana"=>"katsuo"}
p a.sort{|a,b|a[1]<=> b[1]} #=>[["Kudamono", "ichigo"], ["Sakana", "katsuo"], ["Yasai", "nasu"]]
- sortメソッドがソートした「配列」を生成して返すため戻り値は二次元配列になる
- ハッシュで出力したい場合は
to_h
メソッドを使うと良い - Array#to_h (Ruby 3.3 リファレンスマニュアル)
a = {"Yasai" => "nasu", "Kudamono"=>"ichigo", "Sakana"=>"katsuo"}
p a.sort{|a,b|a[1]<=> b[1]}.to_h #=>{"Kudamono"=>"ichigo", "Sakana"=>"katsuo", "Yasai"=>"nasu"}
Dirクラス
-
Dir.delete
,Dir.rmdir
は空のディレクトリを削除する -
Dir.getwd
,Dir.pwd
現在のディレクトリのフルパスを表示(Dir.getwd (Ruby 3.3 リファレンスマニュアル)) - ディレクトリ操作のためのクラス
- ディレクトリのコマンドにありそうなやつだと思っておけば良さそう
- class Dir (Ruby 3.3 リファレンスマニュアル)
file openの際の第2引数
モードの概要
-
r
:読み込みモード(新しいファイルの作成をしようとするとエラーになる。+
があっても同様。 -
w
:書き込みモード -
a
:書き込み追記モード - 上記3つに
+
がつくと読み書き両方使えるモードになる(それぞれ違いはある) - モードを明示しない場合
r
(読み込みモード) になります!
今回使用するファイル
- ファイルを読み込み文字を逆順に出力するために
??
の箇所は何を入れるべきか? - 読み込みと書き込みの両方を行うため
+
付きのものがマスト
File.open("nyan_by_w.txt","??") do |f|
data = f.read.chomp
data.reverse!
f.rewind
f.write data
end
nyan_by_w.txt
Wanko is a friend
+w
の場合
- 実行後
nyan_by_w.txt
- オープン時にファイルがすでに存在していればその内容を空にするため消えてしまう
- 新しいファイルを作成したり、すでに存在するファイルを新たに書き換えることには向いているが、今あるファイルの内容を書き換えるのは不可能
+r
の場合⭕️
- 実行後
nyan_by_w.txt
dneirf a si oknaW
- すでにあるファイルを読み込んで変換するのにはおすすめ
-
+r
にしても新しいファイルは作成できない点注意
+a
の場合
- 実行後
nyan_by_w.txt
Wanko is a frienddneirf a si oknaW
- すでにある文章に付け加えられて出力
問題で登場したメソッド
read メソッド/ chomp メソッド
- ファイルから文字列読み取り
- 改行コードを取り除いた文字列を出力
open("nyan_by_w.txt","r+") do |f|
data = f.read.chomp #=>"Wanko is a frienddneirf a si oknaW"
pp data
data.reverse!
f.rewind
f.write data
end
rewind メソッド
-
ファイルポインタを先頭に移動
-
これしないと
+r
でも追記するような感じになってしまう
# silver_test_book/nyan_by_w.txt
Wanko is a friend
#silver_test_book/sj47.rb
File.open("nyan_by_w.txt","r+") do |f|
data = f.read.chomp
data.reverse!
f.write data
end
# silver_test_book/nyan_by_w.txt
Wanko is a frienddneirf a si oknaW
write メソッド
-
書き込む(カッコつけても同じです)
open("nyan_by_w.txt","r+") do |f|
data = f.read.chomp
data.reverse!
f.rewind
f.write(data)
end
Fileクラスのメソッド
-
File.chmod
メソッド:引数に指定したファイルのモードを変更します。 - File#chmod (Ruby 3.3 リファレンスマニュアル)
-
File.delete
メソッド:引数に指定したファイルを削除します。削除に成功した場合は削除したファイルの数を返し、削除に失敗した場合はエラーが発生します。 - File.delete (Ruby 3.3 リファレンスマニュアル)
-
File.join
メソッド:ファイル名連結する
p File.join("ruby", "exam","silver")#=>"ruby/exam/silver"
- File.join (Ruby 3.3 リファレンスマニュアル)
- File.pwdメソッドとFile.homeメソッドはFileクラスには存在しません。
参考資料
transpose
とzip
メソッド
Arrayクラスの- 問題で結構出てきて似てるな〜と思っていたが全然別物だった
transpose
について
- 自身を行列と見立てて、行列の転置(行と列の入れ換え)を行う。2次元以上の配列であることがマスト
- 引数は取らない!
- 一次元配列の場合例外TypeError。二次元配列でも要素数が異なればIndexErrorになる。
- 空配列なら空配列を返す
- Array#transpose (Ruby 3.3 リファレンスマニュアル)
zip
について
- 自身と引数に渡した配列の各要素からなる配列を生成
- 生成される配列の要素数はselfと同じ
- selfに合わせて引数の要素数が足りない場合はnilで補完される
- 引数がない場合はselfのみの2次元配列になる
- Array#zip (Ruby 3.3 リファレンスマニュアル)
p [1,2,3].zip #=>[[1], [2], [3]]
問題29(問題集)
-
zip
-
自身と引数に渡した配列の各要素からなる配列の配列を生成して返す
-
生成される配列の要素数は self の要素数と同じ(引数に不足あれば
nil
になる) -
ブロック付きで呼び出した場合は、 self と引数に渡した配列の各要素を順番にブロックに渡す
-
-
選択肢2
a = ["a", "b", "c"]
b = [1,2,3]
a.zip(b){|x| p [x]}
#=>["a", 1]
#=>["b", 2]
#=>["c", 3]
a = ["a", "b", "c"]
b = [1,2,3]
a.zip(b).each{|x| p [x]}
- 選択肢にはないがあっている
a = ["a", "b", "c"]
b = [1,2,3]
a.zip(b){|x, y| p [x,y]}
a = ["a", "b", "c"]
b = [1,2,3]
a.zip(b).each{|x, y| p [x,y]}
- zipで他の配列と組み合わせたい時は引数にその組み合わせたい値を入れる
a = ["a", "b", "c", "d"]
b = [1,2,3,4]
p [a,b]
[a,b].zip{|x,y| p [x,y]}
#=>[["a", "b", "c", "d"], [1, 2, 3, 4]]
#=>["a", "b"]
#=>[1, 2]
a = ["a", "b", "c", "d"]
b = [1,2,3,4]
p [a,b]
[a,b].zip.each{|x,y| p [x,y]}
#=>[["a", "b", "c", "d"], [1, 2, 3, 4]]
#=>[["a", "b", "c", "d"], nil]
#=>[[1, 2, 3, 4], nil]
-
transpose
-
自身を行列と見立てて、行列の転置(行と列の入れ換え)を行う
a = ["a", "b", "c"]
b = [1,2,3]
p [a,b].transpose #=>[["a", 1], ["b", 2], ["c", 3]]
[a,b].transpose.each{|x, y| p [x,y]}
#=>["a", 1]
#=>["b", 2]
#=>["c", 3]
a = ["a", "b", "c"]
b = [1,2,3]
p [a,b].transpose #=>[["a", 1], ["b", 2], ["c", 3]]
[a,b].transpose.each{|x| p x}
#=>["a", 1]
#=>["b", 2]
#=>["c", 3]
- ブロック内のxに[]つけると配列in配列になるよ
a = ["a", "b", "c"]
b = [1,2,3]
p [a,b].transpose #=>[["a", 1], ["b", 2], ["c", 3]]
[a,b].transpose.each{|x| p [x]}
#=>[["a", 1]]
#=>[["b", 2]]
#=>[["c", 3]]
do ... end
はp
で出力するとエラーになる
原因
ブロックの{}
とdo ... end
は結合度が異なるため。
今回の問題
p [1, 2, 3].inject{|x, y| x + y ** 2} rescue p $! #=>14
p [1, 2, 3].inject do|x, y| x + y ** 2 end rescue p $! #=>#<ArgumentError: wrong number of arguments (given 0, expected 1..2)>
reduce
)
injectメソッドについて(エイリアス:- Enumerable#inject (Ruby 3.3 リファレンスマニュアル)
- 初期値を指定した場合最初ブロックに渡すのは初期値と最初の要素を渡す
p [1, 2, 3].inject(0){|x, y| x + y ** 2} rescue p $! #=>最初に渡すのは初期値0と1で結果は14
- 初期値を指定ない場合最初ブロックに渡すのは最初の要素と2番目の要素を渡す
p [1, 2, 3].inject{|x, y| x + y ** 2} rescue p $! #=>最初に渡すのは初期値1と2で結果は14
p [1].inject{|x, y| x + y ** 2} rescue p $! #=>1(エラーにはなりません)
-
inject(:+)
は順番に足し算してくれる
p [1, 2, 3, 4, 5].inject(:+) #=>15
-
inject(:*)
は順番にかけ算してくれる
p [1, 2, 3, 4, 5].inject(:*) #=>120
-
inject(:-)
は順番にひき算してくれる
p [1, 2, 3, 4, 5].inject(:-) #=>-13
- 数値比較にも使えます
numbers = [3,89,40,39,29,10,50,59,69]
num = numbers.inject do |i,j|
i > j ? i : j #=>大きい方の値が残る
end
p num #=>89
([1, 2, 3].inject)
が解釈されている
pメソッドの引数として- 分かりやすくカッコをつけてみる
p ([1, 2, 3].inject) do|x, y| x + y ** 2 end rescue p $! #=>#<ArgumentError: wrong number of arguments (given 0, expected 1..2)>
- do…endだと{…}よりも結合度が低いので、pを使うと([1, 2, 3].inject)をpメソッドの引数と理解し、injectメソッドの引数がないと言っている。do以降は無視されている。
p [1, 2, 3].inject(0) do|x, y| x + y ** 2 end rescue p $! #=>#<TypeError: 0 is not a symbol nor a string>
- 初期値の指定した場合は
inject(sym)
の形でないよと言われる
参考
each_cons
とeach_slice
each_cons(n)
- 要素を重複ありで n 要素ずつに区切り、ブロックに渡して繰り返す
- nの個数必ずあるように出力されるので要素の最後が出力された時点で処理は終了となる
p (1..5).each_cons(3){|v| p v }
#=>[1, 2, 3]
#=>[2, 3, 4]
#=>[3, 4, 5]
#=>1...5 今回関係ないけどpメソッドが()の引数のみ出力しているのがわかる例だなと思って
- 要素数より大きいnの場合実行されない
p (1..2).each_cons(3){|v| p v } #=>何も出力されない
each_slice(n)
- n 要素ずつブロックに渡して繰り返す。
- 要素数が n で割り切れないときは、最後の回だけ要素数が減る。
(1..7).each_slice(3){|v| p v }
#=>[1, 2, 3]
#=>[4, 5, 6]
#=>[7]
- 要素数より大きいnの場合でも実行されます
(1..2).each_slice(3){|v| p v } #=>[1, 2]
クラス変数
クラス変数のアクセサメソッドはない
- インスタンスメソッドのように
attr_accessor
,attr_reader
,attr_writer
といったアクセサメソッドはない - そのため外部から書き込みをしたい時や読み込みをしたい際はメソッドの定義が必要です
# 読み込み用
def foo
@@foo
end
# 書き込み用
def foo=(value)
@@foo = value
end
クラス変数が定義されているクラスの全インスタンスで共有される
class Foo
@@foo = 'grass🌱'
def foo
@@foo
end
def foo=(value)
@@foo = value
end
end
foo1 = Foo.new
foo1.foo = 'goat🐐'
foo2 = Foo.new
foo2.foo = 'lion🦁'
puts foo1.foo #=>lion🦁
puts foo2.foo #=>lion🦁
- 全部ライオンになってしまう🦁🦁
- オーバーライドしたクラスでも同様です
class Foo
@@var = 'grass🌱'
def var
@@var
end
def var=(value)
@@var = value
end
end
class Bar < Foo
end
foo = Foo.new
foo.var = 'goat🐐'
bar = Bar.new
bar.var = 'lion🦁'
p foo.var #=>lion🦁
p bar.var #=>lion🦁
インスタンスメソッドの場合
class Foo
@foo = 'grass🌱'
def foo #attr_reader :fooに書き換えOK
@foo
end
def foo=(value) #attr_writer :fooに書き換えOK
@foo = value
end
# 上記二つまとめてattr_accessor :foo にできます
end
foo1 = Foo.new
foo1.foo = 'goat🐐'
foo2 = Foo.new
foo2.foo = 'lion🦁'
puts foo1.foo#=>goat🐐
puts foo2.foo #=>lion🦁
eql?
とequal?
について
eql?
- オブジェクトと other が等しければ真を返す。
-
==
と共通の部分もあるが4==4.0はtrueだが4.eql?(4.0)はfalseになる - ハッシュの2つのキーが等しいかによく使われる(再定義される⭕️)
- クォーテーションの違いは関係なし
a1 = "abc"
a2 = 'abc'
print a1.equal? a2
print a1.eql? a2
#=>falsetrue
equal?
- object_idが一致しているか調べる
- 再定義してはならない
- 下記クラスは同一リテラルは同一オブジェクトとみなされるクラス(Rex回答)
- TrueClass
- FalseClass
- NilClass
- Symbol
- Fixnum→Ruby2.4からFixnum, Bignum は Integer に統合されており、 2.4.0 からはどちらも Integer クラスのエイリアス(参考)
- Float
- Object#equal? (Ruby 3.3 リファレンスマニュアル)
Hash関係(デフォルト値の問題)
Hash#default=
について
- ハッシュのデフォルト値を決められる
- キーに対応する値がないとき
h = Hash.new([])
p h[:a] #=>[]
- デフォルトの設定
h = Hash.new([])
h.default = nil
p h.default #=> nil
p h[:a] #=>nil
- 設定の場合はHash#default= (Ruby 3.3 リファレンスマニュアル)
- 確認する場合は
default
メソッドHash#default (Ruby 3.3 リファレンスマニュアル) - デフォルトで作成された要素はpメソッドでHashの中身を参照する際は対象外になる
h = Hash.new("default value")
h[:a]
h[:b] = 100
p h[:a] #=>"default value"
p h #=>{:b=>100}
Hash#default_proc=
について
- ハッシュのデフォルト値を返す Proc オブジェクト指定できる
- Hash#default_proc= (Ruby 3.3 リファレンスマニュアル)
hash = Hash.new {|h, k| raise(KeyError, "Key #{k} does not exist in hash #{h}") }
hash.default_proc = nil
p hash[:a] #=>nil
- 確認する場合は
default_proc
メソッド Hash#default_proc (Ruby 3.3 リファレンスマニュアル) - ハッシュがブロック形式のデフォルト値を持たない場合 nil を返す
Enumerable#any?
- 真である要素があれば、ただちに true を返します。(=処理が終わる)
- すべての要素が偽である場合に false を返します。(=処理が終わる)
- Enumerable#any? (Ruby 3.3 リファレンスマニュアル)
問題
- ブロックでCountクラスのクラスメソッド(upメソッド)を呼び出している
$val = 0
class Count
def self.up
$val = $val + 1
$val == 3 ? true : false
end
end
[*1..10].any? do
Count.up
end
p $val #=> 3
- 3になったタイミングでtrueになりブロックの処理を終える
10以上のケース
$val = 0
class Count
def self.up
$val = $val + 1
$val == 100 ? true : false
end
end
[*1..10].any? do
Count.up
end
p $val #=> 10
- ブロックは10までなので10回falseを繰り返し処理を終える
[*1..10]
はRangeをArrayにする
%記法
文字列
%
p %(a b c) # => "a b c"
p %(とま と ま) #=>"とま と ま"
ハッシュ
%s
p %s(a) # => :a
p %s(a b) #=>:"a b"
配列
-
%w
/%W
p %w(a b c) # => ["a", "b", "c"]
p %W(a b c) # => ["a", "b", "c"]
p %W(a b c) # => ["a", "b", "c"]
%w(foo\ bar baz)=> ["foo bar", "baz"]
- 空白は配列のスペースです
- 沢山開けてもしっかり詰まります
- 空白作りたいなら
\
です
Hashのメソッド
ハッシュが key をキーとして持つか確認するとき
- Hash#has_key? (Ruby 3.3 リファレンスマニュアル)
- has_key?(key) -> bool
- include?(key) -> bool
- key?(key) -> bool
- member?(key) -> bool
ハッシュが value を値として持つか確認するとき
- Hash#has_value? (Ruby 3.3 リファレンスマニュアル)
- has_value?
- value?
updateメソッド(リテラル:merge!)メソッド(破壊的)
claerメソッドはデフォルト値はそのまま
h = Hash.new("default value")
h[:some] = "some"
p h #=> {:some=>"some"}
h.clear
p h #=> {}
p h.default #=> "default value"
配列のインデックスを表示する場合
each_index
- インデックスのみをブロックに渡します
a = [1, 2, 3]
a.each_index { |n| puts n }
#=>0
#=>1
#=>2
- ブロックの要素2つにしてもエラーにはならず、インデックス→空欄になります。
a = [1, 2, 3]
a.each_index { |n, index| puts n, index }
#=>0
#=>
#=>1
#=>
#=>2
#=>
each_with_index
- 要素とindexをブロックに渡します
a = [1, 2, 3]
a.each_with_index { |n, index| puts n,index }
#=>0
#=>1
#=>1
#=>2
#=>2
#=>3
- Enumerable#each_with_index (Ruby 3.3 リファレンスマニュアル)(Arrayクラスに継承)
Threadクラス
スレッドオブジェクトの生成
Thread.new {} # => #<Thread:0x00007fc5e0836000@(irb):1 run>
Thread.start {} # => #<Thread:0x00007ffaa68110e8@(irb):2 run>
Thread.fork {} # => #<Thread:0x00007f917003cb28@(irb):3 run>
ヒアドキュメントとインデント
通常の場合
s = <<EOF
Hello,
Nakaji
EOF
p s #=>sj37.rb:1: can't find string "EOF" anywhere before EOF (SyntaxError)
- 最後にインデントがあるとエラーになる
s = <<EOF
Hello,
Nakaji
EOF
p s #=>" Hello,\n Nakaji\n"
- 詰めれば良い。インデントはそのまま表示される。
-
の場合
s = <<-EOF
Hello,
Nakaji
EOF
p s #=>" Hello,\n Nakaji\n"
- 最後のインデントあっても問題ない。インデントはそのまま表示される。
~
の場合
s = <<~EOF
Hello,
Nakaji
EOF
p s #=>"Hello,\nNakaji\n"
- 最後インデントあっても問題ない。インデントは削除されて表示される。
クオテーション
- シングルクォート:式展開、改行文字が実行されない
s = <<'EOB'
Hello.\nRuby
World.
EOB
puts s
#=>Hello.\nRuby
#=>World.
p s #=>"Hello.\\nRuby\nWorld.\n"
- ノーマル・ダブルクォート:どちらも有効
s = <<"EOB"
Hello.\nRuby
World.
EOB
puts s
#=>Hello.
#=>Ruby
#=>World.
p s #=>"Hello.\nRuby\nWorld.\n"
- バッククォーテーション:コマンド出力
日付表記関連
- Dateクラス使うには
require "date"
が必要 -
Date.strftime
は引数は1つ
年月日表記(()内は2024年7月16日を表記した場合)
-
%x
=%m/%d/%y
(07/16/24) -
%D
=%m/%d/%y
(07/16/24) -
%F
=%Y-%m-%d
(2024-07-16) - Date#to_s =
%Y-%m-%d
(2024-07-16)
各種表記方法
-
%y
=西暦の下2桁(00-99) -
%Y
=西暦を表す数(9999) -
%m
=月を表す数字(01-12) -
%M
=分(00-59) -
%d
=日(01-31)
ハッシュのキーをクラスにする
- ハッシュのキーはシンボルじゃなくてもできますが今回はクラスをキーにします
- 難しかったのでRexの問題そのまま載せます(参考)
問題
- 下記と同じ結果が得られるプログラムを選びましょう
klass = Class.new
hash = {klass => 100}
puts hash[klass] #=>100
# 参考
puts hash #=>{#<Class:0x00000001028356f0>=>100}
- クラスのオブジェクトがキーになっているのですね
選択肢①
klass = Class.new
hash = {klass: 100}
puts hash[klass] #=>
# 参考
puts hash[:klass] #=>100
puts hash #=>{:klass=>100}
- 別で
:klass
がキーの新しいハッシュを作成してしまっている。
選択肢②
klass = Class.new
hash = {}
hash.store(klass, 100)
puts hash[klass] #=>100
-
store(key, value)
メソッドはキーとバリューを紐づける - Hash#[]= (Ruby 3.3 リファレンスマニュアル)
選択肢③
klass = Class.new
hash = {}
hash.store(:klass, 100)
puts hash[klass] #=>
# 参考
puts hash[:klass] #=>100
puts hash #=>{:klass=>100}
- storeメソッドを使用しているがこれも
:klass
がキーの別のハッシュを作成してしまっている。
選択肢④
klass = Class.new
hash = Hash[klass, 100]
puts hash[klass]
# 参考
puts hash #=>{#<Class:0x0000000102db5c88>=>100}
- 以下は配列からハッシュを生成している。奇数の場合エラーになります。
- Hash.[] (Ruby 3.3 リファレンスマニュアル)
!
じゃないけど破壊的変更)
配列の要素追加・削除色々(
push
(末尾に追加)エイリアス:append
- 指定された obj を順番に配列の末尾に追加する。
- 引数を指定しなければ何もしない
array = [1, 2, 3]
p array.push 4 #=>[1, 2, 3, 4]
p array.push [5, 6] #=>[1, 2, 3, 4, [5, 6]]
p array.push 7, 8 #=>[1, 2, 3, 4, [5, 6], 7, 8]
p array.push #=>[1, 2, 3, 4, [5, 6], 7, 8]
p array #=>[1, 2, 3, 4, [5, 6], 7, 8]
- Array#append (Ruby 3.3 リファレンスマニュアル)
- ちなみにStringクラスに
append
メソッドはない-
破壊的な連結を行いたい場合は
#<<
かconcat
-
pop
(末尾から削除)
- 自身の末尾から要素を取り除いてそれを返す
- 引数を指定した場合はその引数分取り出す
- 空配列の場合、n が指定されていない場合は nil を、指定されている場合は空配列を返す
array = [1, 2, 3]
p array.pop(3) #=> [1,2, 3]
p array.pop #=>nil
p array.pop(1) #=>[]
p array #=>[]
unshift
(先頭に追加)エイリアス:prepend
- 指定された obj を順番に配列の先頭に追加する。
- 引数を指定しなければ何もしない
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]
shift
(末尾から削除)
- 自身の先頭から要素を取り除いてそれを返す
- 引数を指定した場合はその引数分取り出す
- 空配列の場合、n が指定されていない場合は nil を、指定されている場合は空配列を返す
a = [0, 1, 2, 3, 4]
p a.shift #=> 0
p a #=> [1, 2, 3, 4]
p [].shift #=> nil
p [].shift(1) #=> []
IO#seek
- seek(offset, whence = IO::SEEK_SET)
- IO#seek (Ruby 3.3 リファレンスマニュアル)
offset:ファイルポインタを移動させるオフセットを整数で指定
whence:スタート位置
- IO::SEEK_SET: ファイルの先頭から (デフォルト)
- IO::SEEK_CUR: 現在のファイルポインタから
- IO::SEEK_END: ファイルの末尾から
- IO::SEEK_DATA: offset 以降の次にデータがある位置へ(lseek の man ページ参照。Linux 3.1 以降のみ)
- IO::SEEK_HOLE: offset 以降の次にホールがある位置へ(同上)
IO#readlineとIO#gets
- いずれも文字の読み込みのためのメソッド
IO#readline
- 一行読み込んで、読み込みに成功した時にはその文字列を返す
- EOF に到達した時には EOFError が発生する
- IO#readline (Ruby 3.3 リファレンスマニュアル)
IO#gets
- 一行読み込んで、読み込みに成功した時にはその文字列を返す
- EOF に到達した時にはnilを返します
- IO#gets (Ruby 3.3 リファレンスマニュアル)
IO#readlines
- path で指定されたファイルを全て読み込んで、その各行を要素としてもつ配列を返す
- IO.readlines (Ruby 3.3 リファレンスマニュアル)
IO#readlength = nil, outbuf = "")
- length バイト読み込んで、その文字列を返す。
- IO#read (Ruby 3.3 リファレンスマニュアル)
絶対値の取得
通常の絶対値の取得
abs
magnitude
-12345.abs # => 12345
12345.abs # => 12345
-1234567890987654321.abs # => 1234567890987654321
絶対値の2乗の取得
abs2
2.abs2 # => 4
-2.abs2 # => 4
2.0.abs2 # => 4
-2.0.abs2 # => 4
次の整数・次の文字列
整数
succ
next
1.next #=> 2
(-1).next #=> 0
1.succ #=> 2
(-1).succ #=> 0
文字列
- Integerと同じような感じです
- String#next (Ruby 3.3 リファレンスマニュアル)
String#splitメソッド
- ()がない場合とある場合の違い
()がない場合
p "Apple-Banana-Lemon".split /-/ #=>["Apple", "Banana", "Lemon"]
-
-
で分割。配列には-
は含まない。
()がある場合
p "Apple-Banana-Lemon".split /(-)/ #=>["Apple", "-", "Banana", "-", "Lemon"]
-
-
で分割。配列には-
を含む。 - String#split (Ruby 3.3 リファレンスマニュアル)
数値変換系
to_i
メソッド関連
- 0b (2 進数)、0 (8 進数)、0o (8 進数)、0d (10 進数)、0x (16 進数)
puts 0b2 #=>numeric literal without digits (SyntaxError)
- 0, 2 〜 36 以外の引数を指定した場合は例外 ArgumentError が発生します
arr = [
"a".to_i(36), #=>36進数でのa
"070".to_i(0), # =>8進数での70=56
nil.to_i, # = > 0
"0b0001".to_i #=> 0(引数0か2を入れれば1になる)
]
p arr
-
base に 0 を指定するとプリフィクスから基数を判断。基数とプリフィクスの両方を指定しても大丈夫です⭕️
p "1f".to_i(16) # => 31
p "0x1f".to_i(16) # => 31
p "0x1f".to_i(0) # => 31
p "1f".to_i(0) # => 1
- nilは0
p "nil".to_i #=>0
- true,falseはエラー
p "true".to_i #=>undefined method `to_i' for false (NoMethodError)
p "false".to_i #=>undefined method `to_i' for false (NoMethodError)
String#oct
- 文字列を 8 進文字列であると解釈して、整数に変換
- oct は文字列の接頭辞 ("0", "0b", "0B", "0x", "0X") に応じて 8 進以外の変換も行う
- 8進数でない場合は0を返す
puts '10'.oct #=>8
puts 'A'.oct #=>8
puts '0b10'.oct=>2
puts '9'.oct=>0
String#hex
- 文字列に 16 進数で数値が表現されていると解釈して整数に変換
- 接頭辞 "0x", "0X" とアンダースコアは無視
- 文字列に 16 進数で数値が表現されていると解釈して整数に変換します。接頭辞 "0x", "0X" とアンダースコアは無視されます。文字列が [_0-9a-fA-F] 以外の文字を含むときはその文字以降を無視(エラーにならない)
- self が空文字列のときは 0 を返す
p "byz".hex #=>11
p "xyz".hex #=>0
カッコの前のスペース
ない場合
def foo(n)
n ** n
end
puts foo(2) * 2 #=>8
- fooメソッドを引数2で呼び出した戻り値に2をかけるので8
ある場合
def foo(n)
n ** n
end
puts foo (2) * 2 #=>256
- foo ((2) * 2)と解釈される。すなわち4**4=256となる
- 「かっこの手前にスペースが入ると、「式をグループ化するかっこである」と解釈される」らしい(参考:Rubyでメソッド呼び出しのかっこの手前にスペースが入るとエラーになる理由 #プロを目指す人のためのRuby入門 - Qiita)
()関係で追加
arr = Array(3){"a"}
arr.first.upcase!
p arr #=> undefined method `upcase!' for an instance of Integer (NoMethodError)
- Array([3])と定義したのと同じになる。(後ろのブロックは無視される)
arr = Array(3){"a"}
p arr #=>[3]
p arr.first #=>3
- 整数3に対して
upcase!
を呼び出してしまうのでエラーになる
Rubyの定数について
定義
- アルファベット大文字 ([A-Z]) で始まる識別子
変更が可能
- 警告は出るものの変更が可能
MAX = 10
print MAX
MAX = 100
print MAX
#=>10
#=>sj11.rb:3: warning: already initialized constant MAX
#=>sj11.rb:1: warning: previous definition of MAX was here
#=>100
破壊的メソッドの使用
MAX = 'tomato'
p MAX #=>"tomato"
p MAX.upcase! #=>"TOMATO"
p MAX #=>"TOMATO"
- 関係ないけど破壊的メソッドを使用してもobject_idは変わりません(同じ定数だから)
MAX = 'tomato'
p MAX.object_id #=>60
p MAX.upcase! #=>"TOMATO"
p MAX.object_id #=>60
メソッド内で定義するとエラーになります
def hoge
x = 10
Y = x < 10 ? "C" : "D"
puts Y
end
hoge #=>dynamic constant assignment (SyntaxError)
多重代入
左辺より右辺の数が少ない場合
-
nil
が代入される
a, b = 0 # , b => nil
p a #=> 0
p b #=> nil
左辺1で右辺の2以上ある場合
- 配列になる
a = 1, 2 #=> [1, 2]
a #=> [1, 2]
左辺2以上で右辺が3以上ある場合
- 3つ目の要素は無視される
a, b = 1, 2, 3 #=> [1, 2, 3]
a #=> 1
b #=> 2
- 可変長引数使うとこんな感じ
a, *b = 1, 2, 3 #=> [1, 2, 3]
a #=> 1
b #=> [2,3]
演算子の優先順位
- 代入の優先順位はあまり高くない
- 演算子式 (Ruby 2.1.0)
v1 = 1 - 1 == 0
v2 = v1 || raise RuntimeError #ここでv1 || raiseを評価してしまい、syntax error, unexpected constant, expecting `do' or '{' or '(' (SyntaxError) となる
puts v2 && false
改善案
-
=
より優先順位が低いor
を使用する
v2 = v1 or raise RuntimeError
- raiseメソッドの引数(message)であることをカッコをつけて明示する
v2 = v1 || raise(RuntimeError)
- module function Kernel.#fail (Ruby 3.3)
- ()をつけてひとまとまりにする
v2 = v1 || (raise RuntimeError)
**
>*
- 考えてみれば当たり前だったけど掛け算より累乗の方が優先される
p "foo" * 2 **2 #=>"foofoofoofoo"
- 先2の2乗されてそれを文字列に掛ける
&:メソッド名
について
partitionメソッドで動きを確認👀
a, = (1..5).partition(&:odd?)
p a #=>[1, 3, 5]
-
a,
は多重代入になっている。
a,b = (1..5).partition(&:odd?)
p a #=>[1, 3, 5]
p b #=>[2,4]
- ちなみに通常の代入にすると二次元配列になる
a = (1..5).partition(&:odd?)
p a #=>[[1, 3, 5], [2, 4]]
to_proc
で書き換える(上記と同じ処理にする)
odd_numbers, _even_numbers = [*1..5].partition { |num| :odd?.to_proc.call(num) }
p odd_numbers #=>[1,3,5]
- Symbol#to_proc (Ruby 3.3 リファレンスマニュアル)
- 【Ruby】array.map(&:method)を理解する #Ruby - Qiita
- 【Ruby】任意の範囲の数値で配列を作る #Ruby - Qiita
!&:
は使えない
p [1,2,3,4,5].partition(!&:even?) #=> syntax error, unexpected & (SyntaxError)
IOクラスのメソッド
IO.writeメソッド
- 第1引数に指定したファイルを開き、第2引数に指定した文字列の書き込みを行った後ファイル閉じます。第3引数で書き込み開始位置を指定できます。
- IO.write (Ruby 3.3 リファレンスマニュアル)
IO.readlinesメソッド
- path で指定されたファイルを全て読み込んで、その各行を要素としてもつ配列を返します。
- IO.readlines (Ruby 3.3 リファレンスマニュアル)
IO.readメソッド
- path で指定されたファイルを offset 位置から length バイト分読み込んで返します。
- IO.read (Ruby 3.3 リファレンスマニュアル)
- class IO (Ruby 3.3 リファレンスマニュアル)
&
→|
→&&
→||
&&
と||
&
と|
- 演算子(演算子式 (Ruby 3.3 リファレンスマニュアル))
- Arrayクラスのメソッドにも載ってます
- Array#& (Ruby 3.3 リファレンスマニュアル)
-
|
が評価された順に並ぶ(ソートはしない0)
[1, 1, 4, 2, 3] | [5, 4, 5] #=> [1, 4, 2, 3, 5]
問題
a = [1, 2, 5, 7, 8]
b = [1, 3, 6, 7, 9]
c = nil || a & b | a && a | b
p c
ChatGPT解説
申し訳ありません。先ほどの説明で評価順序の部分が誤っていました。実際には、Rubyの演算子の優先順位に従って、&
と|
が&&
や||
よりも優先されます。正しい評価順序を以下に示します。
Rubyの演算子優先順位は以下の通りです(主要なもののみ):
-
&
(ビット演算または配列の交差) -
|
(ビット演算または配列の和集合) -
&&
(論理AND) -
||
(論理OR)
この優先順位に従って、以下のコードを評価します:
a = [1, 2, 5, 7, 8]
b = [1, 3, 6, 7, 9]
c = nil || a & b | a && a | b
p c
評価順序の詳細
-
配列の交差
&
の評価a & b # 結果: [1, 7]
-
配列の和集合
|
の評価(a & b) | a # 結果: [1, 7, 2, 5, 8]
-
論理AND
&&
の評価a && a # 結果: [1, 2, 5, 7, 8]
-
再度配列の和集合
|
の評価((a & b) | a) | b # 結果: [1, 7, 2, 5, 8, 3, 6, 9]
-
論理OR
||
の評価
nil
はfalse
として扱われるため、次の評価に進みます:nil || [1, 7, 2, 5, 8, 3, 6, 9] # 結果: [1, 7, 2, 5, 8, 3, 6, 9]
最終結果
c = [1, 7, 2, 5, 8, 3, 6, 9]
p c # 出力: [1, 7, 2, 5, 8, 3, 6, 9]
このようにして、正しい評価順序に基づいて結果が導かれます。
継承とSuperについて
superとは何か?
- 継承したクラスのメソッドを呼び出すためのもの。通常のメソッド同様引数を取ることも可能
- クラスの継承がなければ使用できない
- 同名のメソッドのみ呼び出せる(継承元の
initialize
メソッドを呼び出すなら継承先のinitialize
メソッドでsuperを呼び出す必要あり)
今回の問題
class Surface
attr_reader :s
def initialize(x,y)
@s = x * y
end
end
class Volume < Surface #VolumeクラスはSurfaceクラスを継承
attr_reader :v
def initialize(x,y,z)
super(x,y) #=>Surfaceクラスのinitializeメソッドを呼び出す。
@v = x * y * z
# super(x,y) ここでもOK
end
end
a = Volume.new(2,5,5)
puts "#{a.v},#{a.s}" #=>50,10
superがなくても初期化されないだけでエラーにはならない
class Surface
attr_reader :s
def initialize(x,y)
@s = x * y
end
end
class Volume < Surface
attr_reader :v
def initialize(x,y,z)
@v = x * y * z
end
end
a = Volume.new(2,5,5)
puts "#{a.v},#{a.s}" #=>50,
initializeメソッド外にsuperあるとエラー
class Surface
attr_reader :s
def initialize(x,y)
@s = x * y
end
end
class Volume < Surface
attr_reader :v
super(x,y)
def initialize(x,y,z)
@v = x * y * z
end
end
a = Volume.new(2,5,5)
puts "#{a.v},#{a.s}"
#=>undefined local variable or method `x' for class Volume (NameError)
superclass
メソッド
- 自身のスーパークラスを返す
- 明示的な継承がない場合は
Object
を返す -
Object
クラスはBasicObject
、BasicObject
はnilを返す。 - Class#superclass (Ruby 3.3 リファレンスマニュアル)
sliceメソッド
- 要素が範囲外の場合、
nil
返す
非破壊的
破壊的
問題36(問題集)
-
slice
(String)p "hogepiyohogehoge".slice(/o../) #=>"oge" p "hogepiyohogehoge".slice(/o.../)#=>"ogep"
- oから始まる
...
の文字数の要素を文頭から探す
- oから始まる
登場した単語の個数数える(ハッシュ)
回答
s = "To be or not to be, that is the question."
hash = Hash.new(0)
s.scan(/\w+/) {|i| hash[i] += 1}
p hash["be"] #=>2
scanメソッド
-
scan
メソッド:self に対して pattern を繰り返しマッチし、マッチした部分文字列の配列を返す - String#scan (Ruby 3.3 リファレンスマニュアル)
s = "To be or not to be, that is the question."
hash = Hash.new(0)
p s.scan(/\w+/) #=>["To", "be", "or", "not", "to", "be", "that", "is", "the", "question"]
- 文字列が単語で区切った配列になりました(正規表現を使用)
- `wは単語構成文字 [a-zA-Z0-9_]の省略記法
ハッシュ
- 空のハッシュが作成
- 先ほどできた配列に対してブロック処理が行われる。
- 最初の単語は"To"。hash["To"] += 1が実行され、ハッシュのキー"To"の値が1になる。
- 最後の行でキーが"be"のバリューを呼び出している
s = "To be or not to be, that is the question."
hash = Hash.new(0)
p hash #=>{}
p s.scan(/\w+/){|i| hash[i] += 1}
p hash #=>{"To"=>1, "be"=>2, "or"=>1, "not"=>1, "to"=>1, "that"=>1, "is"=>1, "the"=>1, "question"=>1}
p hash["be"]#=>2
match
だと最初の1つのみ
s = "To be or not to be, that is the question."
hash = Hash.new(0)
p s.match(/\w+/)#=>#<MatchData "To">
s.match(/\w+/) {|i| hash[i] += 1}
p hash #=>{#<MatchData "To">=>1}
sub
も最初にマッチした部分の置き換え
s = "To be or not to be, that is the question."
hash = Hash.new(0)
s.sub(/\w+/) {|i| hash[i] += 1}
p hash #=>{"To"=>1}
- Stringクラスにsearchメソッドはない
Array#dup
- 配列のコピー(カービィ)
- 代入とは違い異なるオブジェクトになる
foo = [1,2,3]
bar = foo
baz = foo.dup
bar[3] = 4
p foo.object_id #=>60
p bar.object_id #=>60
p baz.object_id #=>80
p foo #=>[1, 2, 3, 4]
p bar #=>[1, 2, 3, 4]
p baz #=>[1, 2, 3]
splitメソッド
str = "100,200,300,400,500"
p str.split(",") #=>["100", "200", "300", "400", "500"]
p str #=>"100,200,300,400,500"
splitメソッドの第2引数
- 配列の個数を指定
- 戻り値は配列になる
str = "a,b,c,d"
p str.split(/,/, 2) #=>["a", "b,c,d"]
p str.split(/,/, 3) #=>["a", "b", "c,d"]
文字列分割と結合
#String#concat
とString#<<
- self に文字列 other を破壊的に連結
- 引数の値が整数である場合は Integer#chr の結果に相当する文字を末尾に追加
- 戻り値もString
- String#<< (Ruby 3.3 リファレンスマニュアル)
to_a
は使えない
文字列で- 必要に応じて定義する
- Object#to_a (Ruby 3.3 リファレンスマニュアル)
Array#join
- 配列の要素を文字列 sep を間に挟んで連結した文字列を返す
- 戻り値はString
ary = ["100", "200", "300", "400", "500"]
p ary.join("\n") #=>"100\n200\n300\n400\n500"
p ary #=> ["100", "200", "300", "400", "500"]
Module#instance_methods
とgrep
メソッド
Module#instance_methods
- そのモジュールで定義されている public および protected メソッド名の一覧を配列で返す。
- Module#instance_methods (Ruby 3.3 リファレンスマニュアル)
grep
メソッド
- ブロックを評価し、一致したものを配列で返す
- Enumerable#grep (Ruby 3.3 リファレンスマニュアル)
p String.instance_methods.grep(/strip/)
#=>[:strip, :lstrip, :rstrip, :strip!, :lstrip!, :rstrip!]
ASCII文字
class NonasciiError < StandardError
end
File.open("sample.txt") do |io|
io.each_line do |str|
begin
raise(NonasciiError, "non ascii character detected") unless str.ascii_only?
rescue => ex
puts "#{ex.message} : #{str}"
end
end
end
#=>non ascii character detected : ルビーアソシエーション
#=>non ascii character detected : るび
sample.txt
Ruby Association
ルビーアソシエーション
るびー
Ruby on Rails
ローカル変数
- 1文字目は小文字アルファベットかアンダーバー
- 2文字目以降はアルファベットもしくは数字を使用
String#delete
、Hash#delete
について
String#delete
- 語順関係なく削除されます
- `a-c' は a から c を意味しする
- "^0-9" のように文字列の先頭が `^' の場合は指定文字以外
puts "Ruby on Rails".delete("Railso") #=>uby n
puts "Ruby on Rails".delete("^Rail")#=>RRail
Hash#delete
- 破壊的メソッドでkey に対応する要素を取り除く
- keyがない場合は
nil
を返す - Hash#delete (Ruby 3.3 リファレンスマニュアル)
Hash#delete_if
(リテラル: reject!
)
- キーと値を引数としてブロックを評価した結果が真であるような要素を self から削除
- Hash#delete_if (Ruby 3.3 リファレンスマニュアル)
正規表現
初心者歓迎!手と目で覚える正規表現入門・その1「さまざまな形式の電話番号を検索しよう」 #JavaScript - Qiita
-問題
/^[hc].*o$/i
#=>Hello,Cello
- 最後の
i
はiオプションをいい小文字・大文字の区別を無視する([JavaScript]正規表現(gオプション,iオプション) #JavaScript - Qiita) -
^[hc]
:文頭はH,C,h,cのいずれか -
.
:改行を除く任意の1文字 -
*
:0回以上の繰り返し -
o$
:文末はOかo
資料
再定義できない演算子
= ?: .. ... not && and || or ::
クラスメソッドより特異メソッドが優先
- クラスを拡張して定義したメソッドよりも特異メソッドが優先される
s = "Hello"
def s.greet
puts "Hi!"
end
class String
def greet
puts "Hello"
end
end
s.greet.class #=>Hi!
p s.singleton_class.method_defined?(:greet)#=>true
p s.class.method_defined?(:greet)"=>true
例外処理
-
例外処理の基本
チェリー本p369〜
begin #例外が起きうる処理 rescue => e #例外オブジェクトを格納する変数 #例外が発生した場合の処理 ensure #例外の有無にか関わらず実行する処理 end
- 仮に例外が起きても最後まで処理を続行できる
-
バックトレースの出し方
-
スタックトレース:エラーが発生する直前に実行していた関数やメソッドのこと
文字リテラル
p ?A #=>"A"
文字リテラル(- リテラル (Ruby 3.3 リファレンスマニュアル))
p ?あ #=>"あ"
puts ?あ #=>あ
- ひらがなでもOK
p ?あい #=>syntax error, unexpected local variable or method, expecting end-of-input (SyntaxError)
- 2文字以上だとエラーになります
配列→文字列
-
最初のクラスは?
a =1,2,3 p a.class #=>Array
-
join
メソッドa =1,2,3 p a.join(",") #=>"1,2,3"
a =1,2,3 p a.join #=>"123" p a.join("-") #=>"1-2-3"
-
to_s
は引数を取らないa =1,2,3 p a.to_s(",") #=>wrong number of arguments (given 1, expected 0) (ArgumentError)
a =1,2,3 p a.to_s #=>"[1, 2, 3]"
- 配列ごと文字列になるイメージ
raiseで引数を指定しない場合
- 引数無しの場合は、同スレッドの同じブロック内で最後に rescue された例外オブジェクト ($!) を再発生させます。
- そのような例外が存在しないが自身は捕捉されている時には例外 RuntimeError を発生させます。
- Rex問題
exceptions = {
NameError => "変数または定数が存在しません",
ArgumentError => "引数が不正です",
RuntimeError => "特定の例外が発生しました"
}
begin
raise
rescue => e
puts exceptions[e.class] #=>特定の例外が発生しました
else
puts "例外は発生しませんでした"
end
このスクラップは4ヶ月前にクローズされました