Closed55

Ruby Silver勉強中のメモ

なかじなかじ

Ruby勉強で便利なデバック

  • Rubyオンリーでアルゴリズムの勉強とかしてる時に、binding.pryみたいの簡単に使えたらいいのに〜と思った時
  • プログラムの冒頭にrequire 'debug'を入れて、メソッドの中身確認したいところにdebuggerbinding.breakと入れると処理が止まる。
  • rdbgコマンドを使用した方法やVSCodeを使用した方法もあるようです
  • debugはRuby2.7以降に導入されているデバック用のライブラリ

https://github.com/ruby/debug

使用する流れ

  • 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か

参考資料

https://techlife.cookpad.com/entry/2021/12/27/202133

なかじなかじ

Rubyのデフォルトの文字エンコード

エンコード・文字コード・文字エンコード

  • エンコード:データを他の形式に変換すること(参考
  • 文字コード:文字に割り当てられた数字(参考)=対応表📝
  • 文字エンコード:文字とコード(数字)の対応表、もしくは、使用する対応表を切り替えること(参考)=対応表をみて変換する人📝👀🗣️

なぜ必要か

  • コンピューターは数字しか理解できないので人間が入力した「あ」とか「V」とかを変換する必要があるため

UTF-8

Rubyデフォルトの文字エンコードはUTF-8

  • 確認方法
puts "なかじ".encoding.name #=>UTF-8
なかじなかじ

文字↔️文字コード

文字→文字コード

p "B".ord #=> 66

文字コード→文字

p 66.chr #=>"B"
  • 引数無しで呼ばれた場合は self を US-ASCII、ASCII-8BIT、デフォルト内部エンコーディングの順で優先的に解釈(エラーにはならない)
  • self を与えられたエンコーディングで正しく解釈できない場合、RangeErrorが発生
  • Integer#chr (Ruby 3.3 リファレンスマニュアル)
なかじなかじ

ハッシュでsortメソッドを使用する際の注意点

前提

戻り値が二次元配列になる

  • 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"]]
a = {"Yasai" => "nasu", "Kudamono"=>"ichigo", "Sakana"=>"katsuo"}
p a.sort{|a,b|a[1]<=> b[1]}.to_h #=>{"Kudamono"=>"ichigo", "Sakana"=>"katsuo", "Yasai"=>"nasu"}
なかじなかじ

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 メソッド
# 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クラスのメソッド

p File.join("ruby", "exam","silver")#=>"ruby/exam/silver"

参考資料

なかじなかじ

Arrayクラスのtransposezipメソッド

  • 問題で結構出てきて似てるな〜と思っていたが全然別物だった

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 と引数に渡した配列の各要素を順番にブロックに渡す

    • Array#zip (Ruby 3.3 リファレンスマニュアル)

  • 選択肢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]
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 ... endpで出力するとエラーになる

原因

ブロックの{}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)>

injectメソッドについて(エイリアス:reduce

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

pメソッドの引数として([1, 2, 3].inject) が解釈されている

  • 分かりやすくカッコをつけてみる
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_conseach_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
h = Hash.new("default value")
h[:a]
h[:b] = 100
p h[:a] #=>"default value"
p h #=>{:b=>100}

Hash#default_proc=について

hash = Hash.new {|h, k| raise(KeyError, "Key #{k} does not exist in hash #{h}") }
hash.default_proc = nil
p hash[:a] #=>nil
なかじなかじ

Enumerable#any?

問題

  • ブロックで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 をキーとして持つか確認するとき

ハッシュが 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
なかじなかじ

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

選択肢③

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}
なかじなかじ

配列の要素追加・削除色々(!じゃないけど破壊的変更)

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]

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

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

IO#gets

IO#readlines

IO#readlength = nil, outbuf = "")

なかじなかじ

数値変換系

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
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

()関係で追加

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]
なかじなかじ

演算子の優先順位

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)
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]

!&:は使えない

p [1,2,3,4,5].partition(!&:even?) #=> syntax error, unexpected & (SyntaxError)
なかじなかじ

IOクラスのメソッド

IO.writeメソッド

IO.readlinesメソッド

IO.readメソッド

なかじなかじ

&|&&||

&&||

&|

[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の演算子優先順位は以下の通りです(主要なもののみ):

  1. &(ビット演算または配列の交差)
  2. |(ビット演算または配列の和集合)
  3. &&(論理AND)
  4. ||(論理OR)

この優先順位に従って、以下のコードを評価します:

a = [1, 2, 5, 7, 8]
b = [1, 3, 6, 7, 9]
c = nil || a & b | a && a | b
p c

評価順序の詳細

  1. 配列の交差 & の評価

    a & b  # 結果: [1, 7]
    
  2. 配列の和集合 | の評価

    (a & b) | a  # 結果: [1, 7, 2, 5, 8]
    
  3. 論理AND && の評価

    a && a  # 結果: [1, 2, 5, 7, 8]
    
  4. 再度配列の和集合 | の評価

    ((a & b) | a) | b  # 結果: [1, 7, 2, 5, 8, 3, 6, 9]
    
  5. 論理OR || の評価
    nilfalse として扱われるため、次の評価に進みます:

    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メソッド

なかじなかじ

登場した単語の個数数える(ハッシュ)

回答

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メソッド

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メソッドはない
なかじなかじ

文字列分割と結合

#String#concatString#<<

文字列でto_aは使えない

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_methodsgrepメソッド

Module#instance_methods

grepメソッド

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#deleteHash#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

Hash#delete_if(リテラル: reject!

なかじなかじ

正規表現

- 初心者歓迎!手と目で覚える正規表現入門・その1「さまざまな形式の電話番号を検索しよう」 #JavaScript - Qiita

-Notionまとめ

問題

/^[hc].*o$/i
#=>Hello,Cello

資料

なかじなかじ

クラスメソッドより特異メソッドが優先

  • クラスを拡張して定義したメソッドよりも特異メソッドが優先される
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
なかじなかじ

例外処理

なかじなかじ

配列→文字列

なかじなかじ

raiseで引数を指定しない場合

  • 引数無しの場合は、同スレッドの同じブロック内で最後に rescue された例外オブジェクト ($!) を再発生させます。
  • そのような例外が存在しないが自身は捕捉されている時には例外 RuntimeError を発生させます。
  • Rex問題
exceptions = {
  NameError => "変数または定数が存在しません",
  ArgumentError => "引数が不正です",
  RuntimeError => "特定の例外が発生しました"
}

begin
  raise
rescue => e
  puts exceptions[e.class] #=>特定の例外が発生しました
else
  puts "例外は発生しませんでした"
end
このスクラップは3ヶ月前にクローズされました