Ruby技術者認定試験 Silver について2

<<識別子行から識別子行の直前までを文字列として扱います。このような表現方法をヒアドキュメントといいます。

今回の問題では、1行目から4行目までがヒアドキュメントとして扱われます。
5行目はただの文字列として解釈されます。

1: s = <<'EOF'
2: Hello,
3: Ruby
4: EOF
5: 'EOF'

識別子の開始ラベルによってヒアドキュメントの解釈の方法が異なります。

開始ラベル 説明
"識別子" 式展開が有効
識別子 ダブルクオートと同じ結果
'識別子' 式展開できない
`識別子` コマンド出力

問題

次のコードを実行するとどうなりますか

s = <<'EOF'
Hello,
Ruby
EOF
'EOF'

p s

 

選択肢

"Hello,\nRuby\n"と表示される

選択肢

"Hello,\nRuby\nEOF"と表示される

選択
選択肢

"Hello,Ruby"と表示される

選択肢

エラーが発生する

 
🔳String#%はフォーマットされた文字列を返します。

フォーマットには、"%d"%sなどの指示子を用います。
String#%を用いると、指示子が引数の値で置換されます。

指示子の詳しい説明はRubyリファレンスを参照してください。

今回の問題では、"%d"が10進数表現で数値を出力します。

 

問題

次のコードを実行するとどうなりますか

p "Hello%d" % 5

 

   p "Hello%d" % 5
"Hello5"
=> "Hello5"

 

選択肢

"HelloHelloHelloHelloHello"と表示される

選択肢

"Hello"と表示される

選択肢

"Hello5"と表示される

選択肢

エラーが発生する

 

 

 

🔳Hash#invertは、キーと値を入れ替え新しいHashを作ります。

問題

次のコードを実行するとどうなりますか

h = {a: 100, b: 200}
p h.invert

選択肢

{100 => :a, 200 => :b}と表示される

選択肢

{200 => :a, 100 => :b}と表示される

選択肢

{:a => 100, :b => 200}と表示される

選択
選択肢

{:a => 200, :b => 100}と表示される

 >> h = {a: 100, b: 200}
=> {:a=>100, :b=>200}
>> p h.invert
{100=>:a, 200=>:b}
=> {100=>:a, 200=>:b}
>> 

 

🔳

  • 空のHashオブジェクトを生成するにはHash({}){}Hash.newのいずれかを用います。
  • Hash#to_hは2次元配列からハッシュを生成します。例えば[[1, "data 1"], [2, "data 2"]].to_hの結果は{1=>"data 1", 2=>"data 2"}になります。
  • Hash#mergeselfと引数のハッシュをマージし、新しいHashを返します。Hash#mergeは非破壊的メソッドです。
  • Hash#clearselfより要素を全て取り除きます。Hash#clearは破壊的メソッドです。

問題

Hashクラスについて適切な記述を選びなさい(複数選択)

選択肢

 

Hash({})で空のHashオブジェクトを生成できる

選択
選択肢

to_hで配列からハッシュを生成できる

選択肢

mergeは破壊的メソッドである

選択肢

clearは非破壊的メソッドである

 

 

🔳productはレシーバーの配列と引数の配列からそれぞれ1つ要素を取り出し新しい配列を作成し、全ての配列を要素とする配列を返します。

productの例

[1, 2].product([3, 4])

# 実行結果
# [[1, 3], [1, 4], [2, 3], [2, 4]]

transposeはレシーバーの配列から行と列を入れ替えた配列を作成し返します。

transposeの例

[[1, 3],
 [1, 4],
 [2, 3],
 [2, 4]
].transpose

# 実行結果
# [[1, 1, 2, 2], [3, 4, 3, 4]]

問題

次のコードを実行するとどうなりますか

arr = [1,2].product([3,4]).transpose
p arr

 

 

選択肢

[[1, 3], [1, 4], [2, 3], [2, 4]]と表示される

選択肢

[[1, 3], [2, 4]]と表示される

選択肢

[1, 2]と表示される

選択肢

[[1, 1, 2, 2], [3, 4, 3, 4]]と表示される

 

 ?> arr = [1,2].product([3,4]).transpose
=> [[1, 1, 2, 2], [3, 4, 3, 4]]
>> p arr
[[1, 1, 2, 2], [3, 4, 3, 4]]
=> [[1, 1, 2, 2], [3, 4, 3, 4]]
>>  

 

🔳

w

not opened for reading (IOError) が発生します。
wはファイルを書き込みモードで開くため、f.readでエラーになります。

a+

recode 1
recode 2
recode 3
RECODE 1
RECODE 2
RECODE 3

a+はファイルを読み込みモード + 追記書き込みモードで開きます。
ファイルの読み込みは、ファイルの先頭から行いますが、書き込みは、ファイルの末尾に行います。
f.rewindでファイルポインタをファイルの先頭に移動したとしても、ファイルの末尾に書き込まれます。

w+

空ファイルになります。

w+は新規作成・読み込み + 書き込みモードで開きます。
既にファイルが存在する場合は、空になります。

r+

RECODE 1
RECODE 2
RECODE 3

r+は読み込み + 書き込みモードで開きます。

 

実行後の textfile.txt 内容になるようにXXXXに適切なコードを選べ

open('textfile.txt', XXXX) do |f|
  data = f.read.upcase
  f.rewind
  f.puts data
end

実行前の textfile.txt 内容

recode 1
recode 2
recode 3

実行後の textfile.txt 内容

RECODE 1
RECODE 2
RECODE 3
選択肢

'w'

選択肢

'a+'

選択
選択肢

'w+'

選択肢

'r+'

 

 

🔳

Enumerable#partitionはブロックの条件を満たす要素と満たさない要素に分割します。

問題では、a, =と指定されているため、Enumerable#partitionの条件を満たす要素がaに入ります。
条件を満たさない要素は変数が指定されていないため、無視されます。

 

 問題

次のコードを実行するとどうなりますか

a, = (1..5).partition(&:odd?)
p a
選択肢

[[2, 4]]と表示される

選択肢

[[1, 3, 5]]と表示される

選択
選択肢

[2, 4]と表示される

選択肢

[1, 3, 5]と表示される

 >> a, = (1..5).partition(&:odd?)
=> [[1, 3, 5], [2, 4]]
>> p a
[1, 3, 5]
=> [1, 3, 5]
>> 

#調べる 表記法(&:odd?)

 

自身が奇数であれば真を返します。 そうでない場合は偽を返します。

例:

5.odd?     # => true
10.odd?    # => false

 https://workout-engineer.com/ruby-block-proc/ 

Procに関するもの。

🔳

..は右辺を含みます
...は右辺を含みません

arr = [1, 2, 3, 4]
p arr[1..2]
p arr[1...2]

# <実行結果>
# [2, 3]
# [2]

 

 

 🔳

Stringto_aメソッドはありません。

p String.method_defined?(:to_a)

# <実行結果>
# false

問題

次のコードを実行するとどうなりますか

hoge = "a".to_a
puts hoge.class
選択肢

Stringと表示される

選択
選択肢

Arrayと表示される

選択肢

Hashと表示される

選択肢

エラーになる

 

🔳

Array#=Array#length以上のインデックスに値を挿入すると、nilで埋めます。

問題コードの実行結果が次の通りです。

1: a = [1]
2: a[5] = 10
3: p a

1: a => [1]
2: a => [1, nil, nil, nil, nil, 10]

問題

次のコードを実行するとどうなりますか

a = [1]
a[5] = 10
p a

 

 🔳

p1p2は別のProcオブジェクトのため、hogeメソッド内のcurrent変数は共有されません。
よって、p2の結果は6になります。

問題

次のコードを実行するとどうなりますか

def hoge(step = 1)
  current = 0
  Proc.new {
    current += step
  }
end

p1 = hoge
p2 = hoge(2)

p1.call
p1.call
p1.call
p2.call
p2.call

p p2.call
選択肢

9と表示される

選択肢

7と表示される

選択
選択肢

6と表示される

選択肢

5と表示される

 

#調べる

 

do ... end{ ... }を比べた場合、{ ... }の方が結合度が強いです。

問題の式の場合、do ... endの結合度が弱いため、p([1, 2, 3, 4].map)が評価されます。
問題のように式の内容を直接使用する際は、{ ... }を使用します。

問題

次のコードを実行するとどうなりますか

p [1,2,3,4].map do |e| e * e end
選択
選択肢

[1, 4, 9, 16]

選択肢

#<Enumerator: [1, 2, 3, 4]:map>

選択肢

nil

選択肢

エラーになる

 

 

可変長引数を2つ定義することはできません。
可変長引数の実行結果は次の通りになります。

def bar(n1, *n2, n3)
  puts "n1: #{n1}, n2: #{n2}, n3: #{n3}" # n1: 5, n2: [6, 7], n3: 8
end

bar 5, 6, 7, 8

問題

実行してもエラーにならないコードを選べ

 

 

🔳s..es...eRangeクラスの定義です。
..は終端を含み、...は終端を含みません。

Enumerator#with_index(offset)は要素にインデックスを添えてを繰り返します。
インデックスはoffsetから開始します。

問題

次のコードを実行するとどうなりますか

(10..15).to_a.map.with_index(1) do |elem, i|
  puts i
end
選択肢
1
2
3
4
5
6
選択
選択肢
0
1
2
3
4
5
選択肢
6
5
4
3
2
1
選択肢
5
4
3
2
1
0

🔳 

Hash#eachのブロックパラメータはArrayで渡されます。

問題

次のコードを実行するとどうなりますか

h = {a: 100, b: 200}
h.each {|p|
  p p.class
}

 

 >> h = {a: 100, b: 200}
=> {:a=>100, :b=>200}
>> h.each {|p|
?> p p.class
>> }
Array
Array
=> {:a=>100, :b=>200}

 

市Array#each_cons(cnt)selfからcnt個ずつ要素を取り出しブロックに渡します。ブロック引数には配列で渡されます。
取り出す要素は、[要素1, 要素2, 要素3], [要素2, 要素3, 要素4] ...と1つづ前に進みます。

似たメソッドにArray#each_slick(cnt)があります。
以下が、それぞれの実行結果です。

(1..10).each_cons(3) {|arr| p arr }

# <実行結果>
# [1, 2, 3]
# [2, 3, 4]
# [3, 4, 5]
# [4, 5, 6]
# [5, 6, 7]
# [6, 7, 8]
# [7, 8, 9]
# [8, 9, 10]
(1..10).each_slice(3) {|arr| p arr }

# <実行結果>
# [1, 2, 3]
# [4, 5, 6]
# [7, 8, 9]
# [10]

問題

次のコードを実行するとどうなりますか

arr = (1..30).to_a
container = []

arr.each_cons(7) do |i|
  container << i
end

p container.length
選択肢

24と表示される

選択肢

5と表示される

選択
選択肢

7と表示される

選択肢

0と表示される

 

🔳Hash#to_aはキーと値の2要素の配列を並べた配列を作成し、返します。

問題

次のコードを実行するとどうなりますか

h = {a: 100, b: 200}
p h.to_a

 

 >> h = {a: 100, b: 200}
=> {:a=>100, :b=>200}
>> p h.to_a
[[:a, 100], [:b, 200]]
=> [[:a, 100], [:b, 200]]

 

🔳

String#==(other)otherが文字列の場合は、String#eql?と同じ結果を返します。
otherが文字列以外の場合は、other.to_sの結果と比較します。

String#eql?は同一文字列の場合にtrueを返します。
ただし、自作クラスの場合はObject#eql?をオーバーライドする必要があります。
さもなければ、同一の内容であるにもかかわらず、オブジェクトIDが異なるためfalseになります。

問題

次のコードを実行するとどうなりますか

a1 = "abc"
a2 = 'abc'

print a1.eql? a2
print a1 == a2

 

>> a1 = "abc"
=> "abc"
>> a2 = 'abc'
=> "abc"
>>
?> print a1.eql? a2
true=> nil
>> print a1 == a2
true=> nil

 

 

🔳メソッドと変数の探索順位は変数が先です。

問題

次のコードを実行するとどうなりますか

hoge = 0
def hoge
  x = 0
  5.times do |i|
    x += 1
  end
  x
end
puts hoge

 

 >> hoge = 0
=> 0
>> def hoge
>> x = 0
>> 5.times do |i|
?> x += 1
>> end
>> x
>> end
=> :hoge
>> puts hoge
0
=> nil

 

String#%はフォーマットされた文字列を返します。
フォーマットには、"%d"%sなどの指示子を用います。
String#%を用いると、指示子が引数の値で置換されます。

指示子の詳しい説明はRubyリファレンスを参照してください。

今回の問題では文字列"Hello"にフォーマットに必要な指示子が無いためそのまま出力されます。

指示子を付与すれば次のように表示されます。

p "Hello%d" % 5 # => Hello5

問題

次のコードを実行するとどうなりますか

p "Hello" % 5

 

 >> p "Hello" % 5
"Hello"
=> "Hello"

 

str.chopは末尾の文字を取り除きます。ただし、文字列の末尾が"\r\n"であれば、2文字とも取り除きます。
破壊的メソッドではないので、selfは影響を受けません。

Stringクラスの似たメソッドの説明を以下にまとめます。

メソッド名 説明
strip 文字列の先頭と末尾の空白文字(\t\r\n\f\v)を取り除きます。
strip! 文字列の先頭と末尾の空白文字(\t\r\n\f\v)を破壊的に取り除きます。
chomp 末尾から改行コードを取り除きます。
chop 末尾の文字を取り除きます。ただし、文字列の末尾が"\r\n"であれば、2文字とも取り除きます。

問題

次のコードを実行するとどうなりますか

str = "Liberty Fish   \r\n"
str.chop
p str

 

 

🔳unlessは条件が成立しない場合に中の処理が実行されます。
elseを用いることはできますが、elsifを用いることはできません。

問題

次のコードを実行するとどうなりますか

def hoge(n)
  unless n != 3
    "hello"
  elsif n == 5
    "world"
  end
end

str = ''
str.concat hoge(3)
str.concat hoge(5)

puts str

 

 🔳

w+

recode 2

w+は新規作成・読み込み + 書き込みモードで開きます。
既にファイルが存在する場合は、空になります。
IO#seekはファイルポインタを指定の位置に移動します。IO:SEEK_SETがファイルの先頭からの位置を指定する識別子です。

よって、recode 1を書き込み後にファイルの先頭にファイルポインタを移動し、recode 2で上書きしています。

w

recode 2

wは書き込みモードで開きます。
その他はw+と同様です。

a+

recode 1
recode 2

a+はファイルを読み込みモード + 追記書き込みモードで開きます。
ファイルの読み込みは、ファイルの先頭から行いますが、書き込みは、ファイルの末尾に行います。

a

recode 1
recode 2

aはファイルを追記書き込みモードで開きます。
ファイルの読み込みを行うことはできません。読み込みを行なった場合は、not opened for reading (IOError)が発生します。

問題

実行後の textfile.txt 内容になるようにXXXXに適切なコードを選べ。
ただし、空ファイルは作成済みである。

File.open('testfile.txt', XXXX) do |f|
  f.write("recode 1\n")
  f.seek(0, IO::SEEK_SET)
  f.write("recode 2\n")
end

実行後の textfile.txt 内容

 recode 1

recode 2

 

🔳

Hash#new(default)は、空のHashを作成します。
default引数が指定された場合は、valuedefaultが入ります。
defaultで作成された要素はpメソッドなどでHashの内容を参照する際は対象外になります。

問題

次のコードを実行するとどうなりますか

h = Hash.new("default value")
h[:a]
h[:b] = 100

p h
選択肢

{:a => "default value", :b => 200}と表示される

選択
選択肢

{:a => nil, :b => 200}と表示される

選択肢

{:b => 100}と表示される

選択肢

エラーが発生する

 

 🔳

Array(3)Array([3])と宣言したことと同じ意味になります。この時ブロックの内容は無視されます。
問題では、Fixnumupcaseメソッドが定義されていないためエラーになります。

Array#firstは配列の先頭要素を取得します。

Array.new(3){"a"}は要素数3の配列を作成し、ブロックの値でそれぞれ初期化します。
Array.new(3, "a")は要素数3の配列を作成し、全要素を"a"で初期化します。この時、"a"は同一のオブジェクトを参照しています。

String#upcaseは非破壊メソッドなので、selfの内容は変わりません。
String#upcase!は破壊メソッドなので、selfの内容が変わります。

問題

実行結果にある結果を得るように選択肢からコードを選べ

#実行結果
["A", "a", "a"]
選択
選択肢
arr = Array(3){"a"}
arr.first.upcase
p arr
選択肢
arr = Array(3){"a"}
arr.first.upcase!
p arr
選択肢
arr = Array.new(3){"a"}
arr.first.upcase
p arr
選択肢
arr = Array.new(3){"a"}
arr.first.upcase!
p arr

 

 

🔳Rubyではメソッド内で定数を定義することができません。
複数回メソッドを呼び出した場合に、定数が不定となるため定義できません。
宣言された場合は、SyntaxErrorが発生します。

問題

以下のコードを実行するとどうなりますか

def hoge
  x = 10
  Y = x < 10 ? "C" : "D"
  puts Y
end
hoge

 

 🔳

Rubyの配列は0番からスタートします。
また、...は終端を含みません。
よって、3番目から4番目のcdが出力されます。

問題

次のコードを実行するとどうなりますか

str = "abcdefghijk"
p str[2...4]

 

 

 

🔳以下のコードはirbやpryではSyntaxErrorとなりますが、通常はSyntaxErrorとはなりません。

1: (1..10).each
2: .reverse_each
3: .each do |i|
4:   puts i
5: end

以下のコードは1, 2行目の行末でバックスラッシュ()を記述することで、1行のコードとみなされます。

1: (1..10).each \
2: .reverse_each \
3: .each do |i|
4:   puts i
5: end

問題

実行してもエラーにならないコードを選べ

選択肢
(1..10).each
.reverse_each
.each do |i|
  puts i
end
選択
選択肢
(1..10).each.
reverse_each.
each do |i|
  puts i
end
選択肢
(1..10).each \
.reverse_each \
.each do |i|
  puts i
end
選択肢
(1..10).to_a.each.
reverse_each.
each do |i|
  puts i
end

 

 "String"に"binary"メソッドはありません。

  • 0xFF0xは16進数を表すプレフィックスです。ここでは16進数を10進数に変換された、255が表示されます。
  • 7.to_s(3)selfを3進数に変換した、21が表示されます。

問題

次のコードのXXXXに記述するとエラーが発生するコードはどれですか

puts XXXX
選択肢

"80"

選択
選択肢

0xFF

選択肢

7.to_s(3)

選択肢

"7".binary

 

🔳Hash#delete(:key)はレシーバーからkeyの項目を削除します。
このメソッドは破壊的メソッドです。

 次のコードを実行するとどうなりますか

h = {a: 100, b: 200}
h.delete(:a)
p h

 

選択肢

{:a => 100, :b => 200}と表示される

選択
選択肢

{:a => nil, :b => 200}と表示される

選択肢

{:b => 200}と表示される

選択肢

エラーが発生する

 

 

🔳Time#+selfに加算します。単位は秒です。

問題

次のコード実行するとどうなりますか

puts Time.now + 3600

 

 

択肢

3600分後の時間を出力する

選択肢

36秒後の時間を出力する

選択肢

60分後の時間を出力する

選択肢

6分後の時間を出力する

 

 

🔳Date#strftimeは引数のフォーマット文字列で日付を文字列で返します。

次の表が選択肢で使用されているフォーマット文字列の意味になります。

フォーマット文字列 意味
%F 日付(%Y-%m-%d)
%Y 西暦4桁
%y 西暦の下2桁(00-99)
%m 月を表す数字(01-12)
%d 日(01-31)

問題

Date.today.strftime("%F")と同じ動作をするコードを選びなさい

選択肢

Date.today.strftime("%y-%m-%d")

選択
選択肢

Date.today.strftime("%y/%m/%d")

選択肢

Date.today.strftime("%Y/%m/%d")

選択肢

Date.today.strftime("%Y-%m-%d")

 

 🔳数値を比較するメソッドの問題です。

Rubyでオブジェクトの比較をするメソッドは大きく分けて==eql?equal?の3つがあります。

レシーバーをFixnumオブジェクトとした場合は次の通りです。

注意:Fixnum、BignumはRuby2.4からIntegerに統合されました。

  • ==

    Fixnumにあるメソッドです。

    数値として等しいかを判定します。11.0は同じ数値として判定され、trueになります。

    p 1 == 1.0 # ture とされる
    
  • eql?

    Numericクラスのメソッドです。

    同じクラスのオブジェクトかつレシーバーにある==メソッドで等しいと判定された場合にtrueになります。

    p 1.eql? 1.0 # 1.0はFloatクラスのオブジェクトなので、`false`になります。
    p 1.eql? (0 + 1) # 計算結果がFixnumクラスのオブジェクトかつ、数値として等しいので、`true`になります。
    
  • equal?

    BasicObjectクラスのメソッドです。  
    オブジェクトIDが同じであれば、trueになります。

    オブジェクトIDとは、オブジェクト毎に割り振られる整数値です。Fixnumなどのイミュータブルオブジェクトは常に同じオブジェクトIDになります。

    p 1.equal? 1.0
    p 1.equal? (1 + 0)
    

この問題の実行結果は次のとおりです。

x = 1
y = 1.0

print x == y # 数値として等しいので、trueになります。
print x.eql? y # 1.0はFloatクラスのオブジェクトなので、falseになります。
print x.equal? y # 1.0は異なるオブジェクトIDを持つので、falseになります。
print x.equal?(1) # 1はどこで参照しても同じオブジェクトIDなので、trueになります。

答えはtruefalsefalsetrueです。

問題

次のプログラムを実行するとどうなりますか。

x = 1
y = 1.0

print x == y
print x.eql? y
print x.equal? y
print x.equal?(1)
選択肢

truetruetruetrueと表示される

選択肢

falsefalsefalsefalseと表示される

選択肢

truefalsefalsefalseと表示される

選択
選択肢

truefalsefalsetrueと表示される