paiza オンラインハッカソン vol.2 をやってみた

またまた Ruby でやってみました。
 

とりあえず何の工夫もないもの

誰でもすぐに考えそうな方法でやってみました。つまりは総当り。

全然ダメですね。詳しい結果はこちら

コード。

height, width = gets.split.map(&:to_i)
@window = []
height.times {@window << gets.chomp.chars.map(&:to_i)}
num = gets.to_i
wgt_size = []
num.times {wgt_size << gets.split.map(&:to_i)}

def check(wgth, wgtw, h, w)
  wgth.times do |wh|
    wgtw.times {|ww| return false unless @window[wh + h][ww + w].zero?}
  end
  true
end

num.times do |i|
  h, w = wgt_size[i]
  co = 0
  (height - h + 1).times do |h1|
    (width - w + 1).times {|w1| co += 1 if check(h, w, h1, w1)}
  end
  puts co
end

ループが 5重になっています。これではデータの量が増えるとどうしようもないですね。
 

工夫する

それぞれのマスは空いているかいないかなので、ビット演算で判定することにします。ただし、与えられたデータは空いている場所が 0 なので、ビットを反転させて処理します。

いや、まだまだ木野ちゃん喜んでくれないですね。詳しい結果はこちら

コード。

height, width = gets.split.map(&:to_i)
wd = []
height.times {wd << (~ gets.chomp.to_i(2) & ("1" * width).to_i(2))}
num = gets.to_i
wgt_size = []
num.times {wgt_size << gets.split.map(&:to_i)}

window = wd.inject {|r, i| r * 2 ** width + i}

num.times do
  h, w = wgt_size.shift
  if h > height or w > width
    puts 0
  else
    co = 0
    widget = 0
    h.times {widget = widget * 2 ** width + ("1" * w).to_i(2)}
    (height - h + 1).times do
      wgt = widget
      (width - w + 1).times do
        co += 1 if window & widget == widget
        widget = widget << 1    #ビットシフト
      end
      widget = wgt << width     #ビットシフト
    end
    puts co
  end
end

ループは 3重です。
 

最終形

もう少し工夫します。配置可能位置がいちばんせまいところだけ考えればいいので、最初からウィジェットの高さに応じてテーブルを作っておきます。さらにメモ化して高速化。

何とか木野ちゃん、よろこんでくれました。自分にはこれ以上考えつかないですね。詳しい結果はこちら。なお、メモ化する前の結果はこちら。いちおうすべて通っていますが、木野ちゃんのよろこび方がちがいますね。

コード。

height, width = gets.split.map(&:to_i)
wnd = []
height.times {wnd << (~ gets.chomp.to_i(2) & ("1" * width).to_i(2))}
num = gets.to_i
wgt_size = []
num.times {wgt_size << gets.split.map(&:to_i)}

table = []
height.times do |i|
  ar = []
  (height - i).times {|j| ar << wnd[j, i + 1].inject(&:&)}
  ar.delete(0)
  table << ar
end

num.times do
  memo = {}
  count = 0
  h, w = wgt_size.shift
  if h <= height and w <= width
    wid = widget = ("1" * w).to_i(2)
    table[h - 1].each do |wd|
      if memo[wd]
        co = memo[wd]
      else
        co = 0
        (width - w + 1).times do
          co += 1 if wd & widget == widget
          widget = widget << 1    #ビットシフト
        end
        memo[wd] = co
        widget = wid
      end
      count += co
    end
  end
  puts count
end

これもループは 3重ですが、要素の数が一気に減っています。さらにメモ化でメモされた場合はループは 2重です。
(追記:少し冗長なところを直したらさらに高速化しました。結果。)
 

解説ページのアルゴリズム

ここにアルゴリズムの公式解説があります。その「O(H^2 W^2 ) の解法」という解説をそのまま Ruby に落としてみたのがこれです。しかしこれ、満点がでないのですけれど(結果はこんな具合です)。ダメじゃないですか。


 

絵文字フィボナッチ

どんな言語かわからないですよね。

🐱 = []
🍇, 🍉 = 0, 1
while 🍉 < 1000
  🍇, 🍉 = 🍉, 🍇 + 🍉
  🐱 << 🍇
end
p 🐱
#=>[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]

Ruby です😄 おわかりでしょうがフィボナッチ😆

Unicode の全絵文字

# * 0 1 2 3 4 5 6 7 8 9 © ® ‼ ⁉ ™ ℹ ↔ ↕ ↖ ↗ ↘ ↙ ↩ ↪ ⌚ ⌛ ⌨ ⏏ ⏩ ⏪ ⏫ ⏬ ⏭ ⏮ ⏯ ⏰ ⏱ ⏲ ⏳ ⏸ ⏹ ⏺ Ⓜ ▪ ▫ ▶ ◀ ◼ ◽ ◾ ☀ ☁ ☂ ☃ ☄ ☎ ☑ ☔ ☕ ☘ ☝ ☠ ☢ ☣ ☦ ☪ ☮ ☯ ☸ ☹ ☺ ♀ ♂ ♈ ♉ ♊ ♋ ♌ ♍ ♎ ♏ ♐ ♑ ♒ ♓ ♠ ♣ ♥ ♦ ♨ ♻ ♿ ⚒ ⚓ ⚔ ⚕ ⚖ ⚗ ⚙ ⚛ ⚜ ⚠ ⚡ ⚪ ⚫ ⚰ ⚱ ⚽ ⚾ ⛄ ⛅ ⛈ ⛎ ⛏ ⛑ ⛓ ⛔ ⛩ ⛪ ⛰ ⛱ ⛲ ⛳ ⛴ ⛵ ⛷ ⛸ ⛹ ⛺ ⛽ ✂ ✅ ✈ ✉ ✊ ✋ ✌ ✍ ✏ ✒ ✔ ✖ ✝ ✡ ✨ ✳ ✴ ❄ ❇ ❌ ❎ ❓ ❔ ❕ ❗ ❣ ❤ ➕ ➖ ➗ ➡ ➰ ➿ ⤴ ⤵ ⬅ ⬆ ⬇ ⬛ ⬜ ⭐ ⭕ 〰 〽 ㊗ ㊙ 🀄 🃏 🅰 🅱 🅾 🅿 🆎 🆑 🆒 🆓 🆔 🆕 🆖 🆗 🆘 🆙 🆚 🇦 🇧 🇨 🇩 🇪 🇫 🇬 🇭 🇮 🇯 🇰 🇱 🇲 🇳 🇴 🇵 🇶 🇷 🇸 🇹 🇺 🇻 🇼 🇽 🇾 🇿 🈁 🈂 🈚 🈯 🈲 🈳 🈴 🈵 🈶 🈷 🈸 🈹 🈺 🉐 🉑 🌀 🌁 🌂 🌃 🌄 🌅 🌆 🌇 🌈 🌉 🌊 🌋 🌌 🌍 🌎 🌏 🌐 🌑 🌒 🌓 🌔 🌕 🌖 🌗 🌘 🌙 🌚 🌛 🌜 🌝 🌞 🌟 🌠 🌡 🌤 🌥 🌦 🌧 🌨 🌩 🌪 🌫 🌬 🌭 🌮 🌯 🌰 🌱 🌲 🌳 🌴 🌵 🌶 🌷 🌸 🌹 🌺 🌻 🌼 🌽 🌾 🌿 🍀 🍁 🍂 🍃 🍄 🍅 🍆 🍇 🍈 🍉 🍊 🍋 🍌 🍍 🍎 🍏 🍐 🍑 🍒 🍓 🍔 🍕 🍖 🍗 🍘 🍙 🍚 🍛 🍜 🍝 🍞 🍟 🍠 🍡 🍢 🍣 🍤 🍥 🍦 🍧 🍨 🍩 🍪 🍫 🍬 🍭 🍮 🍯 🍰 🍱 🍲 🍳 🍴 🍵 🍶 🍷 🍸 🍹 🍺 🍻 🍼 🍽 🍾 🍿 🎀 🎁 🎂 🎃 🎄 🎅 🎆 🎇 🎈 🎉 🎊 🎋 🎌 🎍 🎎 🎏 🎐 🎑 🎒 🎓 🎖 🎗 🎙 🎚 🎛 🎞 🎟 🎠 🎡 🎢 🎣 🎤 🎥 🎦 🎧 🎨 🎩 🎪 🎫 🎬 🎭 🎮 🎯 🎰 🎱 🎲 🎳 🎴 🎵 🎶 🎷 🎸 🎹 🎺 🎻 🎼 🎽 🎾 🎿 🏀 🏁 🏂 🏃 🏄 🏅 🏆 🏇 🏈 🏉 🏊 🏋 🏌 🏍 🏎 🏏 🏐 🏑 🏒 🏓 🏔 🏕 🏖 🏗 🏘 🏙 🏚 🏛 🏜 🏝 🏞 🏟 🏠 🏡 🏢 🏣 🏤 🏥 🏦 🏧 🏨 🏩 🏪 🏫 🏬 🏭 🏮 🏯 🏰 🏳 🏴 🏵 🏷 🏸 🏹 🏺 🏻 🏼 🏽 🏾 🏿 🐀 🐁 🐂 🐃 🐄 🐅 🐆 🐇 🐈 🐉 🐊 🐋 🐌 🐍 🐎 🐏 🐐 🐑 🐒 🐓 🐔 🐕 🐖 🐗 🐘 🐙 🐚 🐛 🐜 🐝 🐞 🐟 🐠 🐡 🐢 🐣 🐤 🐥 🐦 🐧 🐨 🐩 🐪 🐫 🐬 🐭 🐮 🐯 🐰 🐱 🐲 🐳 🐴 🐵 🐶 🐷 🐸 🐹 🐺 🐻 🐼 🐽 🐾 🐿 👀 👁 👂 👃 👄 👅 👆 👇 👈 👉 👊 👋 👌 👍 👎 👏 👐 👑 👒 👓 👔 👕 👖 👗 👘 👙 👚 👛 👜 👝 👞 👟 👠 👡 👢 👣 👤 👥 👦 👧 👨 👩 👪 👫 👬 👭 👮 👯 👰 👱 👲 👳 👴 👵 👶 👷 👸 👹 👺 👻 👼 👽 👾 👿 💀 💁 💂 💃 💄 💅 💆 💇 💈 💉 💊 💋 💌 💍 💎 💏 💐 💑 💒 💓 💔 💕 💖 💗 💘 💙 💚 💛 💜 💝 💞 💟 💠 💡 💢 💣 💤 💥 💦 💧 💨 💩 💪 💫 💬 💭 💮 💯 💰 💱 💲 💳 💴 💵 💶 💷 💸 💹 💺 💻 💼 💽 💾 💿 📀 📁 📂 📃 📄 📅 📆 📇 📈 📉 📊 📋 📌 📍 📎 📏 📐 📑 📒 📓 📔 📕 📖 📗 📘 📙 📚 📛 📜 📝 📞 📟 📠 📡 📢 📣 📤 📥 📦 📧 📨 📩 📪 📫 📬 📭 📮 📯 📰 📱 📲 📳 📴 📵 📶 📷 📸 📹 📺 📻 📼 📽 📿 🔀 🔁 🔂 🔃 🔄 🔅 🔆 🔇 🔈 🔉 🔊 🔋 🔌 🔍 🔎 🔏 🔐 🔑 🔒 🔓 🔔 🔕 🔖 🔗 🔘 🔙 🔚 🔛 🔜 🔝 🔞 🔟 🔠 🔡 🔢 🔣 🔤 🔥 🔦 🔧 🔨 🔩 🔪 🔫 🔬 🔭 🔮 🔯 🔰 🔱 🔲 🔳 🔴 🔵 🔶 🔷 🔸 🔹 🔺 🔻 🔼 🔽 🕉 🕊 🕋 🕌 🕍 🕎 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 🕞 🕟 🕠 🕡 🕢 🕣 🕤 🕥 🕦 🕧 🕯 🕰 🕳 🕴 🕵 🕶 🕷 🕸 🕹 🕺 🖇 🖊 🖋 🖌 🖍 🖐 🖕 🖖 🖤 🖥 🖨 🖱 🖲 🖼 🗂 🗃 🗄 🗑 🗒 🗓 🗜 🗝 🗞 🗡 🗣 🗨 🗯 🗳 🗺 🗻 🗼 🗽 🗾 🗿 😀 😁 😂 😃 😄 😅 😆 😇 😈 😉 😊 😋 😌 😍 😎 😏 😐 😑 😒 😓 😔 😕 😖 😗 😘 😙 😚 😛 😜 😝 😞 😟 😠 😡 😢 😣 😤 😥 😦 😧 😨 😩 😪 😫 😬 😭 😮 😯 😰 😱 😲 😳 😴 😵 😶 😷 😸 😹 😺 😻 😼 😽 😾 😿 🙀 🙁 🙂 🙃 🙄 🙅 🙆 🙇 🙈 🙉 🙊 🙋 🙌 🙍 🙎 🙏 🚀 🚁 🚂 🚃 🚄 🚅 🚆 🚇 🚈 🚉 🚊 🚋 🚌 🚍 🚎 🚏 🚐 🚑 🚒 🚓 🚔 🚕 🚖 🚗 🚘 🚙 🚚 🚛 🚜 🚝 🚞 🚟 🚠 🚡 🚢 🚣 🚤 🚥 🚦 🚧 🚨 🚩 🚪 🚫 🚬 🚭 🚮 🚯 🚰 🚱 🚲 🚳 🚴 🚵 🚶 🚷 🚸 🚹 🚺 🚻 🚼 🚽 🚾 🚿 🛀 🛁 🛂 🛃 🛄 🛅 🛋 🛌 🛍 🛎 🛏 🛐 🛑 🛒 🛠 🛡 🛢 🛣 🛤 🛥 🛩 🛫 🛬 🛰 🛳 🛴 🛵 🛶 🤐 🤑 🤒 🤓 🤔 🤕 🤖 🤗 🤘 🤙 🤚 🤛 🤜 🤝 🤞 🤠 🤡 🤢 🤣 🤤 🤥 🤦 🤧 🤰 🤳 🤴 🤵 🤶 🤷 🤸 🤹 🤺 🤼 🤽 🤾 🥀 🥁 🥂 🥃 🥄 🥅 🥇 🥈 🥉 🥊 🥋 🥐 🥑 🥒 🥓 🥔 🥕 🥖 🥗 🥘 🥙 🥚 🥛 🥜 🥝 🥞 🦀 🦁 🦂 🦃 🦄 🦅 🦆 🦇 🦈 🦉 🦊 🦋 🦌 🦍 🦎 🦏 🦐 🦑 🧀
PC で見るよりスマホとかでの方がきれいに表示されると思います。

表示用の Ruby コード。Gem 'nokogiri' でこのページスクレイピングする。

require 'open-uri'
require 'nokogiri'

url = "https://ja.wikipedia.org/wiki/Unicode%E3%81%AEEmoji%E3%81%AE%E4%B8%80%E8%A6%A7"
Nokogiri.HTML(open(url)).css('td').each do |x|
  st = x.text
  print ('0x' + st[2..-1]).to_i(0).chr("UTF-8") + " " if st[0, 2] == "U+"
end
puts

Ruby FFI でエラトステネスの篩


これまで RubyC言語で「エラトステネスの篩」を書いたので、Ruby FFI で融合させてみました。Ruby FFI で簡単に Ruby から C言語の関数を呼ぶことができます。Linux Mint 18.2, Ruby 2.3.3 で確認しました。

Ruby FFI を使うには libffi が必要ですが、Linux Mint には最初から入っていました。あと、RubyGem 'ffi' が必要なのでインストールして下さい。

まず C言語のソース。
eratosthenes1.c

#include <math.h>

int sieve(int max, int *a) {
    int i, j;
    
    for (i = 2; i <= sqrt(max); i++) {
        if (a[i] == 1) continue;
        for (j = 2; j <= (max / i); j++) a[i * j] = 1;
    }
    
    i = 0;
    for (j = 2; j <= max; j++) {
        if (!a[j]) a[i++] = j;
    }
    
    return i;
}

コンパイルします。

$ gcc -Wall -fPIC -c eratosthenes1.c -o eratosthenes1.o
$ gcc -shared -o eratosthenes1.so eratosthenes1.o

-fPIC オプションはないとエラーが出るので付けました。これでできた eratosthenes1.so を使います。

Ruby 側。
eratosthenes_ffi.rb

require 'ffi'

module MyModule
  extend FFI::Library
  ffi_lib "./eratosthenes1.so"
  attach_function :sieve, [:int, :pointer], :int
end

max = 1000
pointer = FFI::MemoryPointer.new(:int, max + 1)

size = MyModule.sieve(max, pointer)
p pointer.read_array_of_int(size)

結果。

$ ruby eratosthenes_ffi.rb
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,
 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163,
 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251,
 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443,
 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557,
 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647,
 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757,
 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983,
 991, 997]

1000万までの素数を求めてみました(出力なし)。

$ time ruby eratosthenes_ffi.rb

real	0m0.459s
user	0m0.448s
sys	0m0.008s

pure Ruby の場合と比べて、7倍ほどの高速化になりました。


※参考
Home · ffi/ffi Wiki · GitHub
c - How do I handle ruby arrays in ruby ffi gem? - Stack Overflow
Ruby FFIを使ったエクステンションの作り方 - Boost Your Programming!
Ruby-FFIについて調べてみた。(まとめ) - いものやま。

Ruby FFI(メモ) - Marginalia

最長共通部分列(Ruby)

文字列の「部分列」とは、文字列から任意の文字を取り除いたものをいう。文字列 X と Y があって、文字列 Z が X と Y 両方の部分列になっている場合、Z を X, Y の「共通部分列」という。共通部分列の最長のものが、「最長共通部分列」である。たとえば文字列 CATCGA と GTACCGTCA最長共通部分列は CTCA である。

これを Ruby で求めるプログラムを書きました。
LCS.rb

def compute_LCS_table(x, y)
  l = Array.new(x.length + 1) {Array.new(y.length + 1, 0)}
  1.upto(x.length) do |i|
    1.upto(y.length) do |j|
      l[i][j] = if x[i - 1] == y[j - 1]
        l[i - 1][j - 1] + 1
      else
        a, b = l[i][j - 1], l[i - 1][j]
        (a > b) ? a : b
      end
    end
  end
  l
end

def assemble_LCS(x, y, l, i, j)
  return "" if l[i][j].zero?
  if x[i - 1] == y[j - 1]
    assemble_LCS(x, y, l, i - 1, j - 1) + x[i - 1]
  elsif l[i][j - 1] > l[i - 1][j]
    assemble_LCS(x, y, l, i, j - 1)
  else
    assemble_LCS(x, y, l, i - 1, j)
  end
end


x, y = "CATCGA", "GTACCGTCA"
l = compute_LCS_table(x, y)
puts assemble_LCS(x, y, l, x.length, y.length)    #=>CTCA

ここで最初に compute_LCS_table で求めている l[i][j] は、文字列 x[0..(i - 1)], y[0..(j - 1)]最長共通部分列の長さです。これは動的計画法で求めています。それから assemble_LCS で実際の最長共通部分列を求め直しています。
 

アルゴリズムの基本

アルゴリズムの基本

Ruby で迷路作成(コンソール版)

ここでグラフィカルな迷路を生成してみたのですが、やっぱりコンソール版も欲しいよねということで作りました。
 
こんな感じ。

*****************************************
* * * * * * * *
*** * * * ***** * ***** * *** ******* ***
* * * * * * * * * * *
******* * * ******* * *** *** * *** *** *
* * * * * * * * * *
*** ******* * * ***** ***** * * ***** ***
* * * * * * * * * * *
* ******* *** *** ******* * *** * *** * *
* * * * * * *
* ***** * *** ******* * * *** * ***** ***
* * * * * * * * * * * *
*** * * * * * ***** * *** * ***** *** ***
* * * * * * * * * *
*****************************************
どうですかね。

コード。
maze_another.rb

class Maze
  def initialize(x, y)
    raise "maze size error" unless x.odd? and y.odd? and x >= 5 and y >= 5
    @x, @y = x, y
    
    @field = Array.new(y) {Array.new(x, 0)}
    co = 1
    1.step(y - 2, 2) do |j|
      1.step(x - 2, 2) {|i| @field[j][i] = co; co += 1}
    end
    
    @a, @b = (x - 3) / 2, (y - 3) / 2
    @lx, @ly = @a * (@b + 1), (@a + 1) * @b
    @remain_wall = (0...(@lx + @ly)).to_a
  end
  
  def generate
    break_wall until finish
    @field
  end
  
  def break_wall
    wall = @remain_wall[rand(@remain_wall.size)]
    @remain_wall.delete(wall)
    if wall < @lx
      x, y = wall % @a * 2 + 2, wall / @a * 2 + 1
      return if (m = @field[y][x - 1]) == (n = @field[y][x + 1])
    else
      wall -= @lx
      x, y = wall % (@a + 1) * 2 + 1, wall / (@a + 1) * 2 + 2
      return if (m = @field[y - 1][x]) == (n = @field[y + 1][x])
    end
    @field[y][x] = m
    m, n = n, m if m > n
    replace(m, n)
  end
  
  def replace(m, n)
    1.upto(@y - 2) do |y|
      1.upto(@x - 2) {|x| @field[y][x] = m if @field[y][x] == n}
    end
  end
  
  def finish
    a = @field[1][1]
    1.step(@y - 2, 2) do |j|
      1.step(@x - 2, 2) {|i| return false if @field[j][i] != a}
    end
    true
  end
end

def show_maze(field)
  field.each do |line|
    puts line.map {|x| x.zero? ? "*" : " "}.join
  end
end


field = Maze.new(41, 15).generate
show_maze(field)

迷路の生成と表示は分けています。アルゴリズムは前回と同じで、ここのそれを使わせていただきました。なお、迷路の大きさ(Maze.new(x, y) の x, y)は 5 以上の奇数にして下さい。

Swift のクロージャと Ruby の lambda

ここで Swift におけるクロージャの使い方が説明されています。

こちらで同じ内容のコードを再掲するとこんな感じでしょうか。Swift 4.0。

func f(_ a: Int, _ b: Int, _ closure: (Int, Int) -> Int) -> Int { 
    return closure(a, b) 
} 

print(f(1, 2, {a, b in return a + b}))    //=>3
print(f(1, 2, {$0 + $1}))    //=>3
print(f(1, 2, +))            //=>3

print(f(1, 2) {a, b in return a + b})     //=>3

func addNumber(_ number: Int) -> () -> Int {
    var sum = 0
    func add() -> Int {
        sum += number
        return sum
    }
    return add
}

let addTen = addNumber(10)
print(addTen())    //=>10
print(addTen())    //=>20

let addSeven = addNumber(7)
print(addSeven())  //=>7
print(addSeven())  //=>14
print(addTen())    //=>30

 
これらは Ruby の lambda でも基本的に同じことができます。Ruby の lambda はもちろんクロージャで、第一級関数(first class の関数)ですからね。つまり、変数に代入したり、関数の引数になったり返り値になったりできます。

f = ->(a, b, closure) {closure[a, b]}

p f[1, 2, ->(a, b) {a + b}]    #=>3


add_number = ->(number) {
  sum = 0
  -> {sum += number}
}

add_ten = add_number[10]
p add_ten[]    #=>10
p add_ten[]    #=>20

add_seven = add_number[7]
p add_seven[]  #=>7
p add_seven[]  #=>14
p add_ten[]    #=>30

Ruby の簡潔さがここでも出ているのではないでしょうか。

同じことはもちろん JavaScript でも Python でも可能です。


しかし、Swift はいい言語ですね。ここでも見られるように、 Ruby の「ブロック」と同等の機能を、Ruby と同じくらい可読性の高い形で書けるというのはすばらしい。クロージャの中に引数を書くのは、Swift の開発者の言っているとおり、Ruby のパクリですね。いや、上手くパクったと思います。それから、この記事とは関係ないですが、Swift の「オプショナル型」は Ruby にはないものですね。Rubynil 関連のエラーは結構よく出るので、「オプショナル型」ってのはいい考えに思えます。もちろんより安全性が高まるというのはより面倒になるということで、Swift の融通の効かなさがかなわないこともありますが、これは盾の両面で、どちらかというと Swift のこの点の面倒さは大規模開発とかに有利に効いてくるでしょう。Rubynil の安全性を高めるためにいわゆる「ぼっち演算子」(safe navigation operator)を導入したくらいですし。

Ruby の lambda についてはこちらも。