Ruby silver模擬試験誤答一覧 その2

可変長引数は2つ以上なければよい。

可変長引数は先頭か末端になければならないと錯覚していたので、これは発見だった。

def method(ichi,ni,*san,shi,go)
    p ichi
    p ni
    p san
    p shi
    p go
end
method(1,2,3,4,5,6,7,8,9,0)
pyons@LAPTOP-SF87NLCB:/mnt/c/Users/broad/OneDrive/products/Ruby技術者認定試験$ ruby 5-1-1.rb 
1
2
[3, 4, 5, 6, 7, 8]
9
0

Stripは空白文字と「空白」を削除する。

一方chomp,chopは空白文字は削除しない。あくまで「改行コード」のみ。

irb(main):001:0> string = "HelloWorld  \r\n"
=> "HelloWorld  \r\n"
irb(main):002:0> string.strip!
=> "HelloWorld"
irb(main):003:0> string = "HelloWorld  \r\n"
=> "HelloWorld  \r\n"
irb(main):004:0> string.chop!
=> "HelloWorld  "
irb(main):005:0> string = "HelloWorld  \r\n"
=> "HelloWorld  \r\n"
irb(main):006:0> string.chomp!
=> "HelloWorld  "

String#%の動き

Stringの%メソッドはsprintfと同じ動きをする。これはフォーマットを指定するメソッドで、文字列内にフォーマットを指定する指示子を置き、後から引数を指定してその部分を文字列として埋める。

sprintf フォーマット (Ruby 2.6.0)

irb(main):007:0> "Hello Worod" % 200
=> "Hello Worod"
irb(main):008:0> "Hello Worod%d" % 100 
=> "Hello Worod100"
irb(main):009:0> "Hello Worod%d" %  
irb(main):010:0* 
irb(main):011:0* exit
irb(main):001:0> "Hello World%d" % "hoge"
Traceback (most recent call last):
        3: from /home/pyons/.rbenv/versions/2.5.0/bin/irb:11:in `<main>'
        2: from (irb):1
        1: from (irb):1:in `%'
ArgumentError (invalid value for Integer(): "hoge")

指示子が文字列中になかった場合、引数を指定しても何も起こらない。

-EOFを使ったヒアドキュメント

ヒアドキュメントの末端を行端に持ってくる必要性は「-EOF」を使うとないが、その代わり、インデントもヒアドキュメントの空白として認識されてしまう。

Ruby2.3からこの問題を解消する「~EOF」というメソッドが出来たが、試験対象範囲外となる。

String#apendメソッド

はないよ。 Stringに文字を追加できるのは、「<<」と「concat」でどちらも破壊的。「+」が非破壊的。

ただし、Arrayにはある。

irb(main):009:0> p Array.new.methods.grep(/append/)
[:append]
=> [:append]

https://bogdanvlviv.com/posts/ruby/new-aliases-append-to-push-and-prepend-to-unshift-since-ruby-2_5_0.html (2.1.0のときはなかったから気にしなくてよい)

pushのエイリアスと思われる。

ブロック内で新たに定義した変数

a = [1,2,3]
b = "Ruby"
a.each{|b| a[0] = "Enjoy"}
print a[0],b # EnjoyRuby=> nil

ブロックで新たに定義した変数はブロックを超えたスコープでは参照できない。

実は、Rubyの1.9からブロック内の変数のスコープがブロックの中だけになったのであり、Rubyの1.8より前をつかうと「Enjoy2」になるようだ。

【講評】まつもと ゆきひろのRuby検定 | 日経 xTECH(クロステック)

ブロックの外で定義した変数を処理に取り込むことは可能だが、ブロックの中で定義された変数を外に持ち出すことは出来ない。

これはブロックに限らず、do - endにしても同じことが起こる。

def hoge
    out_of_block = "Fugo"
    (1...5).each do |i|
        inside_the_block = "hoge"
        p out_of_block
    end

    puts inside_the_block
end

hoge
pyons@LAPTOP-SF87NLCB:/mnt/c/Users/broad/OneDrive/products/Ruby技術者認定試験$ ruby 5-1-1.rb 
"Fugo"
"Fugo"
"Fugo"
"Fugo"
Traceback (most recent call last):
        1: from 5-1-1.rb:53:in `<main>'
5-1-1.rb:50:in `hoge': undefined local variable or method `inside_the_block' for main:Object (NameError)

ブロックの{|i| i ** i} で定義された、引数i はブロック内で定義された変数として扱われる。

では、ブロックの中で定義された変数名とブロックの外で新しく定義された変数名が同じであった場合どうなるか。 本来ブロックは「前に定義した変数を他のメソッド(スコープ)に持ち出せる」ことが魅力であった。その点を踏まえると、変数の「再定義」のような形で上書きが行われてしまうと、想像することもできる。

しかし、実際には、ブロック内で新たに定義された変数と、ブロックの前で定義された変数はあくまで「別物」として処理される

x = 0
y = %|Hello|
[1,2,3].each do |x|
  print x.to_s
  y = y + x.to_s
end
puts "\r\n#############\r\n"
puts x 
puts y
PS C:\Users\broad\Desktop> ruby .\dir.rb
123
#############
0
Hello123

ご覧の通り、ブロック内で再び定義された変数「x」はブロックを抜けると、再びブロック前の値「0」に戻っている。 一方、ブロック内で定義されず、そのまま処理が行われた「y」はブロックを抜けると、ブロック内の処理の結果がそのまま反映されている。

File.open の引数

オプションを指定できるが、混乱しがちなので整理。

mode 詳細
r 読み込み専用
w 書き込み専用。書き込み前ファイルの内容は空になる
a 追記専用。書き込む位置は常にファイルの末尾
r+ 読み書きモード。書き込み位置はファイル先頭
w+ 読み書きモード。書き込む前にファイルの内容は空になる
a+ 読み書きモード。書き込む位置はファイルの末尾

w+は「読み書き両用モード」でありながらファイルの読み込み時に既に存在していたファイルを空にしてしまう。という「w」の特性も持ち合わせている。

つまり、「w+」モードでは既に保存されていたファイルの内容を読むことは出来ない。w+モードで読み込めるのは「今回のオープンで書き込んだ内容」のみとなる。

一方「a+」モードではrewindを使ってファイルの先頭まで戻れば、既に書き込まれていた内容も含めて読み込むことが出来る。

Ruby is fun!
open('textfile.txt', "a+") do |f|
  data = f.read.upcase
  p data
  f.rewind
  f.puts "Hello Wolrd"
  f.rewind
  data = f.read
  p data
end
pyons@LAPTOP-SF87NLCB:/mnt/c/Users/broad/OneDrive/products/Ruby技術者認定試験$ ruby 5-1-1.rb
"RUBY IS FUN!\n"
"Ruby is Fun!\nHello Wolrd\n"

最初のrewindでは書き込みに備えて、ファイルの末端にポインタが移動しているが、その次のrewindでは読み込みに備えて、ポインタがファイルの頭にきているのがわかる。

再定義できない演算子

かなり頻出っぽい。

名前 詳細
スコープ演算子 :: (ネストさせた位置に定数を定義するとき使った)
代入演算子  == (+=とかの自己代入演算子もダメ)
条件演算子  ?: (三項演算子で使っているやつ)
範囲演算子  .. ...
論理演算子 && || and or not

逆に言うと、==(同値性チェック)はオーバーライド可能だし、+も可能。(実際Integer classとArrayクラスでは動き違うし)、&単体もオーバーライド可能。%も除算で使われているときとsprintfで使っているときがある。

&のオーバーライドの例 => Rubyで使われる記号の意味(正規表現の複雑な記号は除く) (Ruby 2.6.0)

removeっていうメソッドはない。

Search results: remove - Rubyリファレンス

remove_hogehoge というメソッドはあるけど、Array, String, Hashなどには定義されていない。

定数に対する破壊的メソッド

irb(main):001:0> HOGE = "HelloWolrd"
=> "HelloWolrd"
irb(main):003:0> HOGE<<("!!!")
=> "HelloWolrd!!!"

の実行は警告が表示されない。定数は再代入のとき警告を出す。

ちなみに定数は「英大文字で始まっていれば」、後ろが小文字であってもよい。

例外に関する知識

  • 例外が発生したときの例外スタックはRubyではbacktraceと呼ぶ。ただし、backtracestacktraceも指しているものは同じ。「Rubybが入っているほうと覚えよう。」

ruby - What's the difference between a stack-trace and a back-trace? - Stack Overflow

(この記事のコメントを見て気づいたけど、Linuxが生まれたのは1991年の夏で、Rubyの開発開始が1993年なので、実は大差ない。Linuxというとものすごく古いOSに思えるけど、Rubyもなかなか負けていない。)

数値クラスの継承関係

NumericクラスはNumericクラスを継承して、[Integer, Float, Complex, Rational] クラスに分かれている。「Fixnum」クラスは「Bignum」クラスとともに「Integer」クラスを引き継いでいたが、Ruby2.4からIntegerに統合された。

もちろん、試験ではFixnumクラスが存在するRuby2.1が対象であることに留意する必要がある。

irb(main):004:0> Integer.ancestors
=> [Integer, Numeric, Comparable, Object, Kernel, BasicObject]

Integerクラスを再オープンすれば、新しいメソッドを数値リテラルに書き加えることが出来る。

irb(main):002:0> class Integer
irb(main):003:1>  def to_squ
irb(main):004:2>   return self * self
irb(main):005:2> end
irb(main):006:1> end
=> :to_squ
irb(main):007:0> p 4.to_squ
16
=> 16

負の配列の添え字

配列の添え字が負であっても指しているところは変わらない。

irb(main):032:0> array = %w|a b c d e f g h i|
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
irb(main):034:0> array[5..-6]
=> []

変数の再代入

a = [1,2,3]
b = [1,3,5]

c =a
a = b & c #この時点でcはaと同じものを指していた状態からaへの再代入が発生して、aは今までのaと違うobject_idを持つ。
p a + b + c

|| と | の違い

irb(main):003:0> a | b
=> [1, 2, 3, 4, 5, 7]
irb(main):004:0> a || b
=> [1, 2, 3, 4]

|論理和演算子であり、「両方のいずれかに含まれているもの(またはビット演算)」を示すのに対して、||は条件演算子なので、aがfalseでない以上、aが返ってくる。

この問題を間違えた。

irb(main):017:0> a = [1,2,3,4]
=> [1, 2, 3, 4]
irb(main):018:0> b = [1,3,5,7]
=> [1, 3, 5, 7]
irb(main):019:0> p a || b
[1, 2, 3, 4]
=> [1, 2, 3, 4]

ちなみにこの問題にもやや頭を悩ませている。

y = false
y && (raise "faild!")
puts ("Success!!")

この問題は論理演算子である「&&」が使われている。この演算子は両側の条件式がtrueでなければ全体としての「true」を返さないので、左側の評価式が「false」であれば、その時点で「false」を返し、右側の条件式の評価は行わない。

問題文では「&&」が空白になっていて、結構頭を悩ませた。

|| は論理和ではなく条件演算子なのだから、aがfalseかnilでない時点でaが返ってしまう。

Hash#invertはあるけど、Hash#revertはないよ

revertには「もとに戻す」という意味があるらしく、挙動を適切に説明していない。(reverseが動詞) 一方invertは「反対にする」という意味で、こちらのほうが挙動を適切に説明している。