TaKesso's Tech Blog

Web中心の技術メモ

Ruby: Enumeratorクラス

REx - Ruby Examination で解いた問題の解説を自分なりにまとめ直すシリーズです。

問題

実行したらどうなるか

(10..15).to_a.map.with_index(1) do |elem, i|
  puts i
end

解答

正解は
1
2
3
4
5
6
です

解説

Enumerator#with_index

  • with_index(offset = 0) {|(*args), idx| ... } -> object
  • 生成時のパラメータに従って、要素にインデックスを添えて繰り返します。
  • インデックスは offset から始まります

引用: instance method Enumerator#with_index (Ruby 2.1.0)

似てる Enumerable#each_with_index

  • offset 引数を受け取らない

実行して確かめる

p "------------------"
p (10..15).to_a

p "------------------"
(10..15).to_a.map do |elem|
  p elem
end

p "------------------"
(10..15).to_a.map.with_index do |elem, i|
  p [elem, i]
end

p "------------------"
(10..15).to_a.map.with_index(1) do |elem, i|
  p [elem, i]
end

p "------------------"
(10..15).to_a.map.each_with_index do |elem, i|
  p [elem, i]
end
"------------------"
[10, 11, 12, 13, 14, 15]
"------------------"
10
11
12
13
14
15
"------------------"
[10, 0]
[11, 1]
[12, 2]
[13, 3]
[14, 4]
[15, 5]
"------------------"
[10, 1]
[11, 2]
[12, 3]
[13, 4]
[14, 5]
[15, 6]
"------------------"
[10, 0]
[11, 1]
[12, 2]
[13, 3]
[14, 4]
[15, 5]

Ruby: Enumerableモジュール

REx - Ruby Examination で解いた問題の解説を自分なりにまとめ直すシリーズです。

問題

以下の実行結果として正しいものを選択しなさい。

p [1, 2, 3].inject{|x, y| x + y ** 2} rescue p $!
p [1, 2, 3].inject(0){|x, y| x + y ** 2} rescue p $!
p [1, 2, 3].inject([]){|x, y| x << y ** 2} rescue p $!
p [1, 2, 3].inject do|x, y| x + y ** 2 end rescue p $!

選択肢

選択肢 1行目 2行目 3行目 4行目
[1] 14 14 [1, 4, 9] 14
[2] 14 0 [] 14
[3] 14 14 [1, 4, 9]
[4] 14 [1, 4, 9]

解答

正解は [3] です

解説

inject(init = self.first) {|result, item| ... } -> object

  • 最初に初期値 init と self の最初の要素を引数にブロックを実行します。
  • 2 回目以降のループでは、前のブロックの実行結果と self の次の要素を引数に順次ブロックを実行します。
  • そうして最後の要素まで繰り返し、最後のブロックの実行結果を返します。

引用: instance method Enumerable#inject (Ruby 2.1.0)

1行目

begin
  [1, 2, 3].inject {|x, y|
    print sprintf("%d + %d ** 2 = ", x, y)
    p x + y ** 2
  }
rescue
  p $!
end
1 + 2 ** 2 = 5
5 + 3 ** 2 = 14

2行目

begin
  [1, 2, 3].inject(0) {|x, y|
    print sprintf("%d + %d ** 2 = ", x, y)
    p x + y ** 2
  }
rescue
  p $!
end
0 + 1 ** 2 = 1
1 + 2 ** 2 = 5
5 + 3 ** 2 = 14

3行目

begin
  [1, 2, 3].inject([]) {|x, y|
    print sprintf("%s << %d ** 2 = ", x.to_s, y)
    p x << y ** 2
  }
rescue
  p $!
end
[] << 1 ** 2 = [1]
[1] << 2 ** 2 = [1, 4]
[1, 4] << 3 ** 2 = [1, 4, 9]

Ruby: Stringクラスのメソッド

REx - Ruby Examination で解いた問題の解説を自分なりにまとめ直すシリーズです。

問題

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

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

選択肢

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

解答

正解は [4] です

解説

.product

  • 配列からそれぞれ1つ要素を取り出し、新しい配列を作る

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

product 3 4
1 [1, 3] [1, 4]
2 [2, 3] [2, 4]

.transpose

  • 行と列を入れ替えた配列を作る
  • 一次元の配列に対しては、例外 TypeError が発生する
arr = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
].transpose

p arr
# => [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

参考: instance method Array#transpose (Ruby 2.1.0)

問題

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

a = "Ruby"
b = " on Rails"
a.concat b
a.reverse
p a.index("R", 1)

解答

正解は 8 です

解説

String#index(pattern, pos)

  • 文字列のpos番目からpatternを検索し、最初に見つかった位置を返します
  • 位置は左端が0で、1文字ずつ増えてく感じ
R u b y o n R a i l s
0 1 2 3 4 5 6 7 8 9 10 11 12

irbでの実行結果

irb(main):001:0> a = "Ruby"
=> "Ruby"
irb(main):002:0> b = " on Rails"
=> " on Rails"
irb(main):003:0> a.concat b
=> "Ruby on Rails"
irb(main):004:0> a.reverse
=> "sliaR no ybuR"
irb(main):005:0> a
=> "Ruby on Rails"
irb(main):006:0> a.index("R", 1)
=> 8

Ruby: Stringクラスの文字列フォーマット

REx - Ruby Examination で解いた問題の解説を自分なりにまとめ直すシリーズです。

問題

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

p "Hello%d" % 5

選択肢

  1. "HelloHelloHelloHelloHello"と表示される
  2. "Hello"と表示される
  3. "Hello5"と表示される
  4. エラーになる

解答

正解は [3] です

解説

指示子

  • Stringクラスの%はフォーマットされた文字列を返します。
  • 指示子は引数の型の解釈を示します。(指示子 = %の右側の記号)
  • 指示子を省略することはできません。
  • 指示子には大きく分けて3種類あります。
    • 文字列を表す指示子: c, s, p
    • 整数を表す指示子: d, i, u, b, B, o, x, X,
    • 浮動小数点数を表す指示子: f, g, e, E, G

参考: module function Kernel.#format (Ruby 2.1.0)

Ruby: 時刻フォーマット文字列

REx - Ruby Examination で解いた問題の解説を自分なりにまとめ直すシリーズです。

問題

d = Date.new(2015, 1, 5)の時、出力結果が01/05/15になるのはどれ?

選択肢

  1. puts d.strftime("%x")
  2. puts d.strftime("%m/%d/%Y")
  3. puts d.strftime("%m/%D/%y")
  4. puts d.strftime("%M/%d/%y")

解答

正解は [1] です

解説

フォーマット文字列の意味

  • 『年・月・日』
    • %C: 世紀 (2009年であれば 20)
    • %Y: 西暦を表す数
    • %y: 西暦の下2桁(00-99)
    • %B: 月の名称(January, February ... )
    • %b: 月の省略名(Jan, Feb ... )
    • %h: %b と同等
    • %m: 月を表す数字(01-12)
    • %d: 日(01-31)
    • %e: 日。一桁の場合、半角空白で埋める ( 1..31)
  • 『曜日・週数・日数』
    • %A: 曜日の名称(Sunday, Monday ... )
    • %a: 曜日の省略名(Sun, Mon ... )
    • %w: 曜日を表す数。日曜日が0(0-6)
    • %u: 月曜日を1とした、曜日の数値表現 (1..7)
    • %U: 週を表す数。最初の日曜日が第1週の始まり(00-53)
    • %W: 週を表す数。最初の月曜日が第1週の始まり(00-53)
    • %V: ISO 8601形式の暦週 (01..53)
    • %j: 年中の通算日(001-366)
  • 『時・分・秒』
    • %H: 24時間制の時(00-23)
    • %I: 12時間制の時(01-12)
    • %k: 24時間制の時。一桁の場合、半角空白で埋める ( 0..23)
    • %l: 12時間制の時。一桁の場合、半角空白で埋める ( 0..12)
    • %M: 分(00-59)
    • %S: 秒(00-60) (60はうるう秒)
    • %L: ミリ秒 (000.999)
    • %N: 秒の小数点以下。桁の指定がない場合は9桁 (ナノ秒)、%6N: マイクロ秒 (6桁)、%3N: ミリ秒 (3桁)
    • %s: 1970-01-01 00:00:00 UTC からの経過秒
  • 『午前午後』
    • %P: 午前または午後(am,pm)
    • %p: 午前または午後(AM,PM)
  • 『合わせ技』
    • %D: 日付 (%m/%d/%y)
    • %x: 同上
    • %F: %Y-%m-%d と同等 (ISO 8601の日付フォーマット)
    • %v: VMS形式の日付 (%e-%b-%Y)
    • %c: 日付と時刻
    • %X: 時刻
    • %R: 24時間制の時刻。%H:%M と同等。
    • %T: 24時間制の時刻。%H:%M:%S と同等。
    • %r: 12時間制の時刻。%I:%M:%S %p と同等。
  • タイムゾーン
  • 『その他』
    • %n: 改行 (\n)
    • %t: タブ文字 (\t)
    • %%: %自身

参考: instance method Time#strftime (Ruby 2.1.0)

実際の出力

require 'date'

d = Date.new(2015, 1, 5)

puts d.strftime("%x")
puts d.strftime("%m/%d/%Y")
puts d.strftime("%m/%D/%y")
puts d.strftime("%M/%d/%y")
01/05/15
01/05/2015
01/01/05/15/15
00/05/15

Ruby: 2進数/8進数/16進数

REx - Ruby Examination で解いた問題の解説を自分なりにまとめ直すシリーズです。

問題

エラーが発生するのはどれですか

選択肢

  1. puts "0x90".hex
  2. puts '90'
  3. puts 0b2
  4. puts '80'.oct

解答

正解は [3] です

解説

数値のプレフィックス

num = 0d1234       // 10進数 (0dで始まる数値は10進数とみなされる)
num = 0xffff       // 16進数 (0xで始まる数値は16進数とみなされる)
num = 0o777        //  8進数 (0oで始まる数値は 8進数とみなされる)
num = 0b11000100   //  2進数 (0bで始まる数値は 2進数とみなされる)

引用: Ruby入門 - 数値・文字列・型

文字列を数値変換するメソッド

  • Stringクラス
    • .hex: 16進数の文字列表現とみなして、10進数の整数に変換する
    • .oct: 8進数の文字列表現とみなして、   〃
      • oct は文字列のプレフィックスに応じて 8 進以外の変換も行います。
      • p "0b10".oct # => 2
      • p "0x10".oct # => 16
    • .to_i: 10進数の文字列表現とみなして、   〃
    • .to_f: 10進数の文字列表現とみなして、10進数の浮動小数点数 Float に変換する
  • Kernelモジュール
    • Integer(): 引数を整数に変換した結果を返します
    • Float(): 引数を浮動小数点数 Float に変換した結果を返します

参考,引用: class String (Ruby 2.5.0)

実際の実行結果

irb(main):001:0> puts "0x90".hex
144
=> nil
irb(main):002:0> puts '90'
90
=> nil
irb(main):003:0> puts 0b2
SyntaxError: (irb):3: numeric literal without digits
puts 0b2
       ^
        from /your/path/...
irb(main):004:0> puts '80'.oct
0
=> nil

Ruby: ヒアドキュメントの扱い

REx - Ruby Examination で解いた問題の解説を自分なりにまとめ直すシリーズです。

問題

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

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

p s

選択肢

  1. "Hello,\nRuby\n"と表示される
  2. "Hello,\nRuby\nEOF"と表示される
  3. "Hello,Ruby"と表示される
  4. エラーが発生する

解答

正解は [1] です

解説

  • <<'EOF' から EOF までがヒアドキュメント
  • 'EOF' はただの文字列出力
  • p は改行コード¥nを表示する
    • puts / print / printf は実際に改行した文字列を表示する
  • 開始ラベル
    • "識別子": 式展開が有効
    • 識別子: 同上
    • '識別子': 式展開できない
    • `識別子`: コマンド出力