連続数字をハイフンでつなぐ(Ruby)

既に更新はされていませんが、僕はブログ「hp12c」をよく読んでいます。Ruby 好きには楽しいですね。

そこで、「Rubyで連続数字をハイフンでつなぐよ」というエントリがありました。元ネタはここということです。やることは要するに、「スペース区切りの数字列を、数字が連続する場合にその箇所をハイフンにする」ということです。

"1 2 3" => "1-3."
"1 2 3 5 7 8" => "1-3, 5, 7-8."
"1 3 4 5 7" => "1, 3-5, 7."

こんな感じ。で、元ネタのも含めていろいろ Ruby の便利メソッドを使った華麗な回答がいくつもあるのですが、ふつうにというか、あんまり便利メソッドを使わない素直な回答を考えてみました。

こんな感じになりました。

def hyphenize(st)
  ar = st.split.map(&:to_i)
  compress = ->{
    result = ar.first.to_s
    i = 0
    i += 1 while ar[i].succ == ar[i + 1]
    result += "-" + ar[i].to_s if i.nonzero?
    ar = ar.drop(i + 1)
    result
  }
  str = ""
  str += compress.() + ", " until ar.empty?
  str[0..-3] + "."
end

ちょっと長いし華麗でも何でもないですけれど、まあ素直だと思います。是非リンク先の凝った回答たちも見てみて下さい。
追記: クロージャの機能を使って書き換えてみました。変数arクロージャcompress.() に保持されています。


リンク先の回答例の中では、smileruby さんのこれが特に凝っていると思います。ただし、掲載されたコードは Ruby 2.5.1 では動かないので、多少変更しました。

def hyphenize(str)
  nums = str.scan(/\d+/).map(&:to_i).sort
  prev = nums.first
  nums.slice_before do |e|
    prev, prevprev = e, prev
    prevprev.succ != prev
  end.map {|e| e.minmax.uniq.join('-')}.join(', ') + '.'
end 

slice_before とか minmax.uniq.join('-') とか、凝っていますねえ。僕は思わずリファレンス・マニュアルを調べましたよ。
 

おまけ

上記事とは関係ないけれど、その smileruby さんのブログを読んでいたらすごい FizzBuzz を発見。これは今まで見た中の(Ruby での)最短じゃないか。

1.upto(100){|i|puts"#{[:Fizz][i%3]}#{[:Buzz][i%5]}"[/.+/]||i}

記事はこちら。わかるまでしばらく考えましたよ。

追記。さらに過去のブログ記事を読んでいたら、これよりも短いのも可能らしい(参照)。ひゃー、すごい世界だな。