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

Hashでblockを使うと

ブロック内部では各要素が配列(1つ目にキー、2つ目に値)の形で渡される。

irb(main):005:0> h.each {|p| p p }
[:a, 100]
[:b, 200]
=> {:a=>100, :b=>200}

Integer#step

では、ループの中で自分自身を含む。

(例:1.step(5,1)という使われ方をしていたら、最初のブロックの|i|は1である。つまりiが「1,2,3,4,5」とインクリメントするよう5回のループが発生する。)

stepという言葉から、最初のループから一回インクリメントされた値がブロックに渡されそうだが、実際はそんなことはない。

条件式としての範囲式

一体どこで活用するのかよくわからないが、不思議な評価の仕方をする範囲式。

範囲式を使ったフリップフロップ - Qiita

素敵な解説記事があった。

問題はこのようなもの

10.times{|d| print d == 3..d == 5 ? "T" : "F" }

範囲式とは完全に別物と考えること!範囲の概念はここでは用いない。

まず最初に左側の条件式(式1とする)が真になるまで、この条件式はfalseを返し続ける。今回の場合「d = 0,1,2」までがfalseとなる。

「d = 3」で式1がtrueになると、今度は右側の条件式(式2とする)が真となるまで「true」を返し続ける。今回の場合「d = 3,4」まではtrueを返す。

「d =5」で式2がtrueになると、式全体としてはtrueを返す。ただし、式1も式2も今までの値をリセットする。

「d = 6、7、8、9」で式1はfalseを返し続ける。

追記)Feature #5400: Remove flip-flops in 2.0 - Ruby master - Ruby Issue Tracking System

なにぃ・・・

Hash#invertでキーが重複した場合。

後ろに出てきた要素のinvertが返るよ。

irb(main):019:0> p ({prefecture: "KANAGAWA", "address": "KANAGAWA"}).invert
{"KANAGAWA"=>:address}
=> {"KANAGAWA"=>:address}

putsの引数にしてエラーが出るのはどれか

超頻出と思われる。

  • "10".oct

octメソッドは原則8進数への変換を行うが、接頭辞に応じてn進数への変換を行える。

irb(main):026:0> puts "0x1a3".oct
419
irb(main):027:0> puts "0b0010".oct
2
irb(main):030:0> puts "0o10".oct
8

これと対照的なのがhexで、こちらは接頭辞を無視して、16進数に直す。16進数と認められない文字があればそれ以降は無視する。 * 080

接頭辞の0自体が「8進数」を示すものであるので、そのあとに「8」が続いてしまうので×。

irb(main):032:0> puts 070
56
=> nil
  • "110".to_i(2)

to_sに引数を渡すと、n進数と解釈して、その10進数表記を返してくれる。

irb(main):036:0> "0xccc".to_i(16)
=> 3276

Aray#transpose

trasposeメソッドは2つの配列が入った配列の行と列の入れ替えを行うメソッド。

irb(main):037:0> a = ["a","b","c"]
=> ["a", "b", "c"]
irb(main):038:0> b = [1,2,3]
=> [1, 2, 3]
irb(main):040:0> [a,b].transpose
=> [["a", 1], ["b", 2], ["c", 3]]

この2つの配列を入れ子配列ではなく、普通の()にしてメソッドを実行する問題があるが、これは動作しないので間違い。

irb(main):016:0> (a,b).transpose.each do |i|
irb(main):017:1*  p i
irb(main):018:1> end
SyntaxError: (irb):16: syntax error, unexpected '.', expecting '='
(a,b).transpose.each do |i|
      ^
        from /home/pyons/.rbenv/versions/2.1.0/bin/irb:11:in `<main>'

String#indexの第2引数

に指定するのは「探索開始位置」。

String#deleteの挙動

instance method String#delete (Ruby 2.6.0)

問題はこちら

puts "0123456789-".delete("^13-56-")

^が接頭辞としてついている文字列は削除しない。(それ以外をすべて削除している)^がついているものに関しては、直後の「文字」だけでなく、「文字列」が対象となる点に注意したい。

これは範囲をとっている文字にも言えることで、

irb(main):001:0> "ABCDEFG".delete("A-Z")
=> ""
irb(main):002:0> "ABCDEFG".delete("^A-Z")
=> "ABCDEFG"

下の例では、^をA-Zに指定したため、文字列は一文字も削除されることなく残っている。 実際の問題文では

 puts "0123456789-".delete("^13-56-")

という式が出ていた。1と3456とハイフンだけは生き残り、それ以外がすべて削除される。

また、範囲指定は「文字列の両端にない場合だけ」である。更にdeleteメソッドは帰り値が「削除が完了した文字列」ではなく、「削除した文字列」である点に注意する必要がある。

これは誤り。String#deleteの戻り値「削除が完了した文字列」。削除を行った文字列を戻り値とするメソッドはArray#deleteとかArray#delete_atとかHash#deleteなど。

Ruby2.2以前のHash宣言

Ruby2.2より前だと、「キーと値のペアを:を使って書くときに限り」キーに文字列を指定することが出来なかったようだ。

pyons@LAPTOP-SF87NLCB:~$ ruby -v
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-linux]
pyons@LAPTOP-SF87NLCB:~$ irb
irb(main):001:0> a = {"a" => "b"}
=> {"a"=>"b"}
irb(main):002:0> a = {"a":"b"}
SyntaxError: (irb):2: syntax error, unexpected ':', expecting =>
a = {"a":"b"}
         ^
(irb):2: syntax error, unexpected '}', expecting end-of-input
        from /home/pyons/.rbenv/versions/2.1.0/bin/irb:11:in `<main>'

もちろんこれより新しいRubyではこの書き方はサポートされている。

pyons@LAPTOP-SF87NLCB:~$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]
pyons@LAPTOP-SF87NLCB:~$ irb
irb(main):001:0> a = {"a":"b","c":"d"}
=> {:a=>"b", :c=>"d"}

Dir#extname

は実装されていない。Fileクラスの実装である。Dir#openディレクトリを開くメソッドとして実装されている。

引数に「*」記号

配列のオブジェクトを返してくれるらしい。 可変長引数としてはやったけど、メソッドに引数を送っているわけではなく、代入を行っているだけなので、このような使い方があるのを知らなかった。

irb(main):001:0> a = *"b"
=> ["b"]
irb(main):002:0> p a
["b"]
=> ["b"]
irb(main):003:0> a = *"b","c","d"
=> ["b", "c", "d"]
irb(main):004:0> p a
["b", "c", "d"]
=> ["b", "c", "d"]

また、引数としてメソッドで渡す場合、配列に変換が行われた後で、配列そのものとして渡されるのではなく、配列の要素一つ一つが順番に渡される。

Hashは配列のネストの形にto_aを用いて変更することが出来る。

hash = {a: 100, b: 200}

def splat_hash(a, b)
  p a
  p b
end

splat_hash(*hash)

この問題では、メソッド呼び出しの時に、ハッシュがto_aで配列化され、[[:a, 100], [:b, 200]]となった後で、各要素([:a, 100]と[:b, 200])が、それぞれ仮引数aとbに代入されている。

String#index

indexメソッドは2つ目の引数に数字をとると、検索をその文字列から始めるが、検索開始時点を0にリセットしているわけではない。あくまで、戻り値はインデックスとしての絶対値である。

IO#seek

seekメソッドの引数には必ず2つの引数を指定する。教科書には「一番目の引数で指定した数だけ、2番目の引数の場所から移動する。」と書いてある。ただし、指定しなければ、先頭を表す「SEEK_SET」が割り当てられる。

定数 意味
SEEK_SET ファイルの先頭
SEEK_CUR 現在のポインタの位置
SEEK_END ファイルの末端

複数の例外クラスを一つのrescue節で捕捉する

には、先ほどやったように、「配列の要素一つ一つを引数として渡す」ことのできるsplat演算子を利用して、rescue節に記述する。

begin
 putss "HelloWorld"
rescue *[StandardError, SyntaxError, ArgumentError]
 puts "Rescued!"
end
pyons@LAPTOP-SF87NLCB:/mnt/c/Users/broad/OneDrive/products/Ruby技術者認定試験$ ruby 5-1-1.rb 
Rescued!

Dateクラス

Dateクラスは組み込みクラスでない、添付ライブラリのはずなので出題範囲でないというのが一応の建前だが、Rexでよく出題されてしまうので、一応抑えておく。

require "date"

d = Date.new(2015,1,5)
puts d.strftime("%F") #=>2015-01-05
require "date"

d = Date.new(2015,1,5)
puts d.strftime("%Y-%m-%d")
require "date"

d = Date.new(2015,1,5)
puts d.strftime("%x") #=> 01/05/15
t = Time.new(2015,1,5)
puts t.strftime("%x") #=> 01/05/15

String#splitの挙動

irb(main):002:0> "a b c d".split()
=> ["a", "b", "c", "d"]
irb(main):003:0> "a\nb\nc\nd".split()
=> ["a", "b", "c", "d"]
irb(main):004:0> "a\nb\nc\nd".split(//)
=> ["a", "\n", "b", "\n", "c", "\n", "d"]
irb(main):006:0> "a\tb\tc\td".split(//)
=> ["a", "\t", "b", "\t", "c", "\t", "d"]
irb(main):007:0> "a b c d".split(//)
=> ["a", " ", "b", " ", "c", " ", "d"]

splitの引数に何も指定しなければ「空白」や「空白文字」も切り分けの対象に含まれる。 一方、splitの引数に//を指定すると、「すべての文字が一つの要素を構成する配列」に変換される。 この「すべての文字」とは空白文字や空白を含む。