AOJ (Introduction to Programming)

AIZU ONLINE JUDGE: Programming Challenge

ITP1_1_A

puts "Hello World"

ITP1_1_B

x = gets.to_i
puts x ** 3

ITP1_1_C

a, b = gets.split.map(&:to_i)
puts "#{a * b} #{2 * (a + b)}"

ITP1_1_D

n = gets.to_i
puts "#{n / 3600}:#{(n % 3600) / 60}:#{n % 60}"

 
ITP1_2_A

a, b = gets.split.map(&:to_i)
st = if    a < b then "<"
     elsif a > b then ">"
     else "=="
     end
puts "a #{st} b"

ITP1_2_B

a, b, c = gets.split.map(&:to_i)
puts (a < b and b < c) ? "Yes" : "No"

ITP1_2_C

puts gets.split.map(&:to_i).sort.join(" ")

ITP1_2_D

w, h, x, y, r = gets.split.map(&:to_i)
st = if x - r >= 0 and y - r >= 0 and x + r <= w and y + r <= h
  "Yes"
else
  "No"
end
puts st

 
ITP1_3_A

1000.times {puts "Hello World"}

IPT1_3_B

i = 0
until (x = gets.to_i).zero?
  puts "Case #{i += 1}: #{x}"
end

ITP1_3_C

loop do
  x, y = gets.split.map(&:to_i).sort
  break if x.zero? and y.zero?
  puts "#{x} #{y}"
end

ITP1_3_D

a, b, c = gets.split.map(&:to_i)
cnt = 0
a.upto(b) {|i| cnt += 1 if (c % i).zero?}
puts cnt

 
ITP1_4_A

a, b = gets.split.map(&:to_i)
printf "%d %d %.5f", a / b, a % b, a.to_f / b

ITP1_4_B

r = gets.to_f
pi = Math::PI
printf "%.6f %.6f", pi * r ** 2, 2 * pi * r

ITP1_4_C

until /\d+ \? \d+/.match(st = gets)
  puts eval(st)
end

ITP1_4_D

gets
a = gets.split.map(&:to_i)
puts "#{a.min} #{a.max} #{a.inject(&:+)}"

 
ITP1_5_A

loop do
  h, w = gets.split.map(&:to_i)
  break if h.zero? and w.zero?
  h.times {puts "#" * w}
  puts
end

ITP1_5_B

loop do
  h, w = gets.split.map(&:to_i)
  break if h.zero? and w.zero?
  st = "#" * w
  puts st
  (h - 2).times {puts "#" + "." * (w - 2) + "#"}
  puts st
  puts
end

ITP1_5_C

loop do
  h, w = gets.split.map(&:to_i)
  break if h.zero? and w.zero?
  st1 = ("#." * w)[0, w]
  st2 = ("." + st1)[0, w]
  h.times {|i| puts [st1, st2][i % 2]}
  puts
end

ITP1_5_D

n = gets.to_i

i = 1
while i <= n
  x = i
  if (x % 3).zero?
    print " #{i}"
  else
    until x.zero?
      if x % 10 == 3
        print " #{i}"
        break
      else
        x /= 10
      end
    end
  end
  i += 1
end
puts

 
ITP1_6_A

gets
puts gets.split.reverse.join(" ")

ITP1_6_B

n = gets.to_i
cards = []
%W(S H C D).each do |suit|
  1.upto(13) {|i| cards << "#{suit} #{i}"}
end
n.times {cards -= [gets.chomp]}
puts cards

ITP1_6_C

n = gets.to_i
rooms = Array.new(4) {Array.new(3) {Array.new(10, 0)}}
n.times do
  b, f, r, v = gets.split.map(&:to_i)
  rooms[b - 1][f - 1][r - 1] += v
end
st = Array.new(4, "")
4.times do |b|
  3.times do |f|
    10.times do |r|
      st[b] += " " + rooms[b][f][r].to_s
    end
    st[b] += "\n"
  end
end
print st.join("#" * 20 + "\n")

ITP1_6_D

n, m = gets.split.map(&:to_i)
a, c = [], []
n.times {a << gets.split.map(&:to_i)}
m.times {c << gets.to_i}

n.times do |i|
  sum = 0
  m.times {|j| sum += a[i][j] * c[j]}
  puts sum
end

 
ITP1_7_A

loop do
  m, f, r = gets.split.map(&:to_i)
  break if m == -1 and f == -1 and r == -1
  sum = m + f
  st = if m == -1 or f == -1
    "F"
  elsif sum >= 80
    "A"
  elsif sum >= 65
    "B"
  elsif sum >= 50
    "C"
  elsif sum >= 30
    (r >= 50) ? "C" : "D"
  else
    "F"
  end
  puts st
end

ITP1_7_B

loop do
  n, x = gets.split.map(&:to_i)
  break if n.zero? and x.zero?
  cnt = 0
  [*1..n].combination(3) do |ar|
    cnt += 1 if ar[0] + ar[1] + ar[2] == x
  end
  puts cnt
end

ITP1_7_C

r, c = gets.split.map(&:to_i)
m = []
r.times {m << gets.split.map(&:to_i)}
tate = Array.new(c, 0)
putout = ->(ar) {
  st = ""
  sum = 0
  ar.each_with_index do |x, i|
    st += x.to_s + " "
    sum += x
    tate[i] += x
  end
  st += sum.to_s
  puts st
}
m.each {|x| putout.(x)}
putout.(tate)

ITP1_7_D

n, m, l = gets.split.map(&:to_i)
a, b = [], []
n.times {a << gets.split.map(&:to_i)}
m.times {b << gets.split.map(&:to_i)}

a.each do |row|
  c = Array.new(l, 0)
  row.each_with_index do |x, i|
    l.times {|j| c[j] += x * b[i][j]}
  end
  puts c.join(" ")
end

 
ITP1_8_A

st = ""
gets.chomp.each_byte do |byte|
  st += if byte.between?(97, 122)
    (byte - 32).chr
  elsif byte.between?(65, 90)
    (byte + 32).chr
  else
    byte.chr
  end
end
puts st

ITP1_8_B

loop do
  x = gets.chomp
  break if x == "0"
  puts x.chars.map(&:to_i).inject(&:+)
end

ITP1_8_C

result = Array.new(26, 0)
$stdin.readlines.map(&:chomp).join.downcase.each_byte do |byte|
  next unless byte.between?(97, 122)
  result[byte - 97] += 1
end
result.each_with_index {|x, i| puts "#{(i + 97).chr} : #{x}"}

ITP1_8_D

s, p = gets.chomp, gets.chomp
ring = s + s
l = p.length
result = "No"
s.length.times do |i|
  if ring[i, l] == p
    result = "Yes"
    break
  end
end
puts result

 
ITP1_9_A

w = gets.chomp
t = ""
loop do
  st = gets.chomp
  break if st == "END_OF_TEXT"
  t += st.downcase + " "
end

puts t.split.count(w)

ITP1_9_B

loop do
  deck = gets.chomp
  break if deck == "-"
  gets.to_i.times do
    i = gets.to_i
    deck = deck[i..-1] + deck[0, i]
  end
  puts deck
end

ITP1_9_C

taro = hanako = 0
gets.to_i.times do
  t, h = gets.chomp.split
  if t > h
    taro += 3
  elsif t < h
    hanako += 3
  else
    taro += 1
    hanako += 1
  end
end
puts "#{taro} #{hanako}"

ITP1_9_D

str = gets.chomp
gets.to_i.times do
  command = gets.split
  a = command[1].to_i
  b = command[2].to_i
  case command[0]
  when "print"
    puts str[a..b]
  when "reverse"
    str = str[0, a] + str[a..b].reverse + str[b + 1..-1]
  when "replace"
    str = str[0, a] + command[3] + str[b + 1..-1]
  end
end

 
ITP1_10_A

x1, y1, x2, y2 = gets.split.map(&:to_f)
puts Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)

ITP1_10_B

include Math

a, b, c = gets.split.map(&:to_i)
θ = c / 180.0 * PI
h = b * sin(θ)

puts a * h / 2
puts a + b + sqrt(a ** 2 + b ** 2 - 2 * a * b * cos(θ))
puts h

ITP1_10_C

#標準偏差
loop do
  n = gets.to_i
  break if n.zero?
  s = gets.split.map(&:to_i)
  m = s.inject(&:+) / n.to_f
  sum = 0
  s.each {|i| sum += (i - m) ** 2}
  puts Math.sqrt(sum / n)
end

ITP1_10_D

n = gets.to_i
x = gets.split.map(&:to_i)
y = gets.split.map(&:to_i)
d = ->(p) {
  sum = 0
  n.times {|i| sum += (x[i] - y[i]).abs ** p}
  sum ** (1 / p.to_f)
}

1.upto(3) {|i| puts d.(i)}
puts x.zip(y).map {|x, y| (x - y).abs.to_f}.max

 
ITP1_11_A

dice = gets.split.map(&:to_i)
commands = gets.chomp.chars.map(&:to_sym)
throw = {E: [3, 2, 6, 1, 5, 4], N: [5, 1, 3, 4, 6, 2],
         S: [2, 6, 3, 4, 1, 5], W: [4, 2, 1, 6, 5, 3]}
th = {}
throw.each_key {|k| th[k] = throw[k].map(&:pred)}

commands.each do |c|
  next_dice = Array.new(6, 0)
  dice.each_with_index do |d, i|
    next_dice[th[c][i]] = d
  end
  dice = next_dice
end
puts dice[0]

ITP1_11_B

possible_appearances = ->(dice) {
  table = 
     [[0, 1, 2, 3, 4, 5], [3, 1, 0, 5, 4, 2], [1, 5, 2, 3, 0, 4], [5, 1, 3, 2, 4, 0],
      [1, 2, 0, 5, 3, 4], [3, 5, 1, 4, 0, 2], [5, 4, 2, 3, 1, 0], [2, 1, 5, 0, 4, 3],
      [1, 0, 3, 2, 5, 4], [5, 2, 1, 4, 3, 0], [2, 4, 0, 5, 1, 3], [4, 5, 3, 2, 0, 1],
      [3, 4, 5, 0, 1, 2], [4, 0, 2, 3, 5, 1], [1, 3, 5, 0, 2, 4], [2, 0, 1, 4, 5, 3],
      [0, 4, 3, 2, 1, 5], [4, 2, 5, 0, 3, 1], [4, 3, 0, 5, 2, 1], [2, 5, 4, 1, 0, 3],
      [3, 0, 4, 1, 5, 2], [0, 3, 1, 4, 2, 5], [0, 2, 4, 1, 3, 5], [5, 3, 4, 1, 2, 0]]
  result = []
  table.each do |dice1|
    another = Array.new(6, 0)
    dice1.each_with_index {|n, i| another[n] = dice[i]}
    result << another
  end
  result.uniq
}

appearances = possible_appearances.(gets.split.map(&:to_i))
gets.to_i.times do
  top, front = gets.split.map(&:to_i)
  appearances.each do |a|
    if a[0] == top and a[1] == front
      puts a[2]
      break
    end
  end
end

ITP1_11_C

possible_appearances = ->(dice) {
  table = 
     [[0, 1, 2, 3, 4, 5], [3, 1, 0, 5, 4, 2], [1, 5, 2, 3, 0, 4], [5, 1, 3, 2, 4, 0],
      [1, 2, 0, 5, 3, 4], [3, 5, 1, 4, 0, 2], [5, 4, 2, 3, 1, 0], [2, 1, 5, 0, 4, 3],
      [1, 0, 3, 2, 5, 4], [5, 2, 1, 4, 3, 0], [2, 4, 0, 5, 1, 3], [4, 5, 3, 2, 0, 1],
      [3, 4, 5, 0, 1, 2], [4, 0, 2, 3, 5, 1], [1, 3, 5, 0, 2, 4], [2, 0, 1, 4, 5, 3],
      [0, 4, 3, 2, 1, 5], [4, 2, 5, 0, 3, 1], [4, 3, 0, 5, 2, 1], [2, 5, 4, 1, 0, 3],
      [3, 0, 4, 1, 5, 2], [0, 3, 1, 4, 2, 5], [0, 2, 4, 1, 3, 5], [5, 3, 4, 1, 2, 0]]
  result = []
  table.each do |dice1|
    another = Array.new(6, 0)
    dice1.each_with_index {|n, i| another[n] = dice[i]}
    result << another
  end
  result.uniq
}

dice1 = gets.split.map(&:to_i)
dice2 = gets.split.map(&:to_i)
puts possible_appearances.(dice1).include?(dice2) ? "Yes" : "No"

ITP1_11_D

possible_appearances = ->(dice) {
  table = 
     [[0, 1, 2, 3, 4, 5], [3, 1, 0, 5, 4, 2], [1, 5, 2, 3, 0, 4], [5, 1, 3, 2, 4, 0],
      [1, 2, 0, 5, 3, 4], [3, 5, 1, 4, 0, 2], [5, 4, 2, 3, 1, 0], [2, 1, 5, 0, 4, 3],
      [1, 0, 3, 2, 5, 4], [5, 2, 1, 4, 3, 0], [2, 4, 0, 5, 1, 3], [4, 5, 3, 2, 0, 1],
      [3, 4, 5, 0, 1, 2], [4, 0, 2, 3, 5, 1], [1, 3, 5, 0, 2, 4], [2, 0, 1, 4, 5, 3],
      [0, 4, 3, 2, 1, 5], [4, 2, 5, 0, 3, 1], [4, 3, 0, 5, 2, 1], [2, 5, 4, 1, 0, 3],
      [3, 0, 4, 1, 5, 2], [0, 3, 1, 4, 2, 5], [0, 2, 4, 1, 3, 5], [5, 3, 4, 1, 2, 0]]
  result = []
  table.each do |dice1|
    another = Array.new(6, 0)
    dice1.each_with_index {|n, i| another[n] = dice[i]}
    result << another
  end
  result.uniq
}

solve = ->(dices) {
  s = dices.size
  (s - 1).times do |i|
    appearances = possible_appearances.(dices[i])
    (i + 1).upto(s - 1) do |j|
      return "No" if appearances.include?(dices[j])
    end
  end
  "Yes"
}

n = gets.to_i
dices = []
n.times {dices << gets.split.map(&:to_i)}
puts solve.(dices)

パスカルの三角形(Haskell)

その1。

main :: IO ()
main = print $ map pascal [0..8]

pascal :: Int -> [Int]
pascal 0 = [1]
pascal n = [1] ++ eachCons (pascal (n - 1)) ++ [1]
       
eachCons :: [Int] -> [Int]
eachCons xs = if length xs < 2
                then []
                else (head xs + (head . tail) xs): (eachCons . tail) xs

結果。

[[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1],[1,5,10,10,5,1],[1,6,15,20,15,6,1],[1,7,21,35,35,21,7,1],[1,8,28,56,70,56,28,8,1]]

 

その2。

main :: IO ()
main = putStr $ unlines $ map (centering 30 . toString) (map pascal [0..8])

centering :: Int -> String -> String
centering n str = (replicate i ' ') ++ str ++ (replicate j ' ')
    where l = length str
          i = div (n - l) 2
          j = n - l - i

toString :: [Int] -> String
toString xs = tail $ concat $ map f xs
    where f x = ' ': show x

pascal :: Int -> [Int]
pascal 0 = [1]
pascal n = [1] ++ eachCons (pascal (n - 1)) ++ [1]
       
eachCons :: [Int] -> [Int]
eachCons xs = if length xs < 2
              then []
              else (head xs + (head . tail) xs): (eachCons . tail) xs

結果。

              1
             1 1
            1 2 1
           1 3 3 1
          1 4 6 4 1
        1 5 10 10 5 1
      1 6 15 20 15 6 1
     1 7 21 35 35 21 7 1
    1 8 28 56 70 56 28 8 1

unlines 関数:[String]を改行を入れて結合する。[String] -> String。
concat 関数:リストの平坦化。
div 関数:整数/整数をして整数で返す。
 

※参考
Hakell I/Oアクションの紹介

elementary OS 0.4.1 Loki をインストール


 
インストール・ファイルはここからダウンロードします。「価格はあなた次第」とありますので、お金を払ってもよいという人は手続きして下さい。無料で使いたい場合は「カスタム」を選択し、0 を入力してインストールすればいいです。

インストールそのものは Debian 系のそれなので、Ubuntu などをインストールしたことのある人はまったく同じです。やり方は検索すればいくらでも出てくるので書きません。マルチブートにつては過去記事に多少詳しく書きました。


インストール終了後、立ち上げてまずネット接続します。これも Ubuntu と同じです。無線LAN はたぶんパスワードの入力だけで簡単に繫がるでしょう。

まずはシステムのアップデートから。

$ sudo apt update
$ sudo apt upgrade

 
このままでは日本語環境になっていないので、日本語環境に設定します。[Complete Installation] はクリックしておいた方がよいでしょう。
20180314205410
設定したらログアウト→再ログインして下さい。メニューなどが日本語になっている筈です。

日本語入力。mozc をインストールします。

$ sudo apt-get install fcitx-mozc

インストール終了後、再起動で有効化します。

ファイルマネージャは「Nemo」を使いたかったのであるが、OS との相性が悪いらしくバグが出るので諦める。

簡易IDE の「Geany」をインストール。ただしこのままだと組み込みの端末が使えないので、

$ sudo apt-get install libvte9

で OK。

Ruby のインストール。2.5.0 を入れる。
Linux Mint に rbenv で Ruby を入れる - Camera Obscura
さらに Bundler を入れて必要な Gem を入れるまでにはすごく時間がかかるので、諦めて待つ。

  • OpenGL 関連
    • $ sudo apt-get install libgl1-mesa-dri libglu1-mesa freeglut3 libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev
  • Gosu 関連
    • $ sudo apt-get install build-essential libsdl2-dev libsdl2-ttf-dev libpango1.0-dev libgl1-mesa-dev libopenal-dev libsndfile-dev libmpg123-dev libgmp-dev
  • RMagick 関連
    • $ sudo apt-get install libmagickwand-dev

 
elementaryOS は Mac っぽいオシャレさが売りですが、デフォルトのアプリケーションは少ないですね。必要なものは自分で入れていかないといけないようです。


※参考
Elementary OSを入れて最初にやったこと & 入れたツール16個

最短ヌクレオチド連鎖問題

require 'bundler/setup'
require 'kaki/utils/nest_loop'

class Codon
  include Comparable
  
  def initialize(st)
    @cd = st
  end
  attr_reader :cd
  
  def <=>(a)
    x, y = @cd, a.cd
    l = r = 0 
    l = if x[1..2] == y[0..1]
      2
    elsif x[2] == y[0]
      1
    else
      0
    end
    r = if y[1..2] == x[0..1]
      2
    elsif y[2] == x[0]
      1
    else
      0
    end
    r - l
  end
  
  def inspect
    @cd
  end
  alias :to_s :inspect
end

def calc(x, y)
  if x.cd[1..2] == y.cd[0..1]
    2
  elsif x.cd[2] == y.cd[0]
    1
  else
    0
  end
end

a = [*0..3]
codon = [a, a, a].nest_loop {|x| x.join}.map {|x| Codon.new(x)}
max = 0

loop do
  c = codon.shuffle.sort
  20.times do
    co = 0
    c.each_cons(2) {|x, y| co += calc(x, y)}
    if max < co
      max = co
      puts 64 * 3 - max
      p c
    end
    c1 = c.sort
    break if c1 == c
    c = c1
  end
end

 
これまでの最小値。

105
[123, 231, 113, 132, 320, 200, 001, 120, 201, 010, 211, 100, 003, 033, 002, 023,
 013, 133, 333, 330, 303, 031, 313, 332, 102, 021, 101, 012, 223, 232, 203, 321,
 301, 331, 311, 112, 322, 220, 202, 212, 210, 230, 030, 300, 000, 020, 011, 111,
 110, 130, 302, 122, 032, 323, 233, 312, 022, 222, 221, 121, 213, 131, 310, 103]

挿入ソートのベンチマーク(Ruby)

Rubyっぽく書いたのと教科書の擬似コードをそのまま書いたものとの比較。

require 'benchmark'

class Array
  def insertion_sort1
    ar = self
    (size - 1).times do |i|
      key = ar[i + 1]
      j = ar.index {|x| x >= key}
      next if j >= i + 1
      ar = ar[0...j] + [key] + ar[j..i] + ar[i + 2..-1]
    end
    ar
  end
  
  def insertion_sort2
    (size - 1).times do |j|
      key = at(j + 1)
      i = j
      while i >= 0 and at(i) > key
        self[i + 1] = at(i)
        i -= 1
      end
      self[i + 1] = key
    end
    self
  end
end

ar = [*0..10000].shuffle
Benchmark.bm do |x|
  x.report {ar.dup.insertion_sort1}
  x.report {ar.dup.insertion_sort2}
end

1 の方は非破壊的で安定でない、2 の方は破壊的で安定。
結果。

       user     system      total        real
   1.430000   0.070000   1.500000 (  1.498388)
   2.330000   0.000000   2.330000 (  2.330626)

Rubyっぽく書いた方が約1.5倍速かった。

もうひとつ思いついた。破壊的で安定でない。

class Array
  def insertion_sort3
    (size - 1).times do |i|
      key = at(i + 1)
      j = index{|x| x >= key}
      if j != i + 1
        delete_at(i + 1)
        insert(j, key)
      end
    end
    self
  end
end

結果。

       user     system      total        real
   1.370000   0.080000   1.450000 (  1.451847)
   2.390000   0.000000   2.390000 (  2.386278)
   1.250000   0.000000   1.250000 (  1.250350)

これがいちばん速かった。意外。
 

アルゴリズムイントロダクション 第3版 第1巻: 基礎・ソート・データ構造・数学 (世界標準MIT教科書)

アルゴリズムイントロダクション 第3版 第1巻: 基礎・ソート・データ構造・数学 (世界標準MIT教科書)

Ruby GTK 覚え書き

これは使えそう。STDIN を必要なファイルディスクリプタにすればよい。

require 'bundler/setup'
require 'gtk2'

w = Gtk::Window.new
w.set_size_request(200, 50)
w.set_resizable(false)
    
b = Gtk::VBox.new
w.add(b)
entry = Gtk::Entry.new
entry.set_editable(false)
b.pack_start(entry)

ioc = GLib::IOChannel.new(STDIN)
ioc.add_watch(GLib::IOChannel::IN) do |io|
  st = io.readline.chomp
  entry.set_text(st)
  true    #繰り返す
end

context = GLib::MainContext.default
mainloop = GLib::MainLoop.new(context, true)

w.signal_connect("destroy") {mainloop.quit}
w.show_all

mainloop.run
ioc.close

参考:
https://github.com/taf2/ruby-gnome2/blob/master/glib/sample/iochannel.rb#L24
C言語/GTKでファイルやらソケットやらのfdが読み込み(or書き込み)可能になるのを待ちたい。 - BlankTar
 

サンプル(9/17)

サーバー側。こちらが先に起動。

require 'bundler/setup'
require 'gtk2'
require 'socket'

module Sample
  class MyWindow < Gtk::Window
    def initialize
      super("Sample")
      set_size_request(200, 60)
      set_resizable(false)
      
      entry = Gtk::Entry.new
      entry.set_editable(false)
      entry.set_height_request(40)
      entry.modify_font(Pango::FontDescription.new("13"))
      add(entry)
      
      io = (ARGV[0] == "-s") ? TCPServer.open(29753) : STDIN
      
      Thread.new(io) do |io|
        ioc = GLib::IOChannel.new((io.class == IO) ? io : io.accept)
        ioc.add_watch(GLib::IOChannel::IN) do |i|
          st = i.gets
          next unless st
          entry.set_text(st.chomp)
          true    #繰り返し
        end
      end
      
      signal_connect("destroy") {Gtk.main_quit}
      show_all
    end
  end
  
  def self.run
    MyWindow.new
    Gtk.main
  end
end

Sample.run

 
クライアント側。

require 'socket'

TCPSocket.open("localhost", 29753) do |sock|
  st = "Hello, World!"
  st.length.times do |i|
    sock.puts st[0, i + 1]
    sleep(1)
  end
end

単純にこんなのでも。

require 'socket'

TCPSocket.open("localhost", 29753) do |sock|
  loop {sock.puts(gets)}
end

Enumerator への追加は deep copy されるのか(Ruby)

どうもそうなのではないかと思って確かめてみたら、やはりそうだった。これは助かる。

$ pry
[1] pry(main)> e = Enumerator.new do |y|
[1] pry(main)*   a = ["aiueo", "Ruby"]
[1] pry(main)*   y << a
[1] pry(main)*   a[0] = "oeuia"
[1] pry(main)*   y << a
[1] pry(main)*   a[0][1] = "nya"
[1] pry(main)*   y << a
[1] pry(main)* end  
=> #<Enumerator: ...>
[2] pry(main)> loop {p e.next}
["aiueo", "Ruby"]
["oeuia", "Ruby"]
["onyauia", "Ruby"]
=> #<Enumerator::Yielder:0x0056471aa90738>

つまりオブジェクトの完全なスナップショットを取れるということ。

いや、ちがうな。

[3] pry(main)> e.rewind
=> #<Enumerator: ...>
[4] pry(main)> loop {p e.next.object_id}
47431695117340
47431695117340
47431695117340
=> #<Enumerator::Yielder:0x0056471aad58b0>

object_id はすべて同じだ。そうか、Enumerator は Fiber を使って実装されているのだから、y << a した時点で止まってしまうのだな。で、e.next されるたびに更新されると。だから deep copy ではない。実際、to_a してみればわかる。

[5] pry(main)> e.rewind
=> #<Enumerator: ...>
[6] pry(main)> e.to_a
=> [["onyauia", "Ruby"], ["onyauia", "Ruby"], ["onyauia", "Ruby"]]

古い Linux Kernel をインストールする

Ubuntuでのカーネルのダウングレード - ひよっこエンジニアの日記
この記事が参考になる。
 
apt でインストール可能な linux-image の一覧。「generic」を選ぶならこんな感じ。

$ apt-cache search linux-image | grep generic

インストールは例えば

$ sudo apt install linux-image-4.4.0-116-generic

みたいにやればいいのだろうと思う。

あとは GRUB を書き換えないといけない。Ubuntu ならたぶん grub-customizer を使うのがラク