/ #ruby 

Ruby2.7のpattern matchingで四則演算と数値リテラルを使わずにFizzBuzz

Ruby2.7のpattern matchingを使うと四則演算と数値リテラルを使わずにFizzBuzzできる。

<!–いきなり追記–>

超絶技巧の人が140文字で教えてくれました! ありがとうございます!

<!–追記ここまで–>

パターンマッチ試してみた。

とにかく

  • スプラットが強力
  • Array Patternが強力

という印象を持った。

こんな感じで書ける!長いけど。

NOT_F = /\A[^F]+\z/ 
NOT_B = /\A[^B]+\z/ 
def fizzbuzz(a)
  a << ''
  case a
  in [*, /B/, _, /F/, _, _, x]          ; x << 'FizzBuzz'
  in [*, NOT_F, NOT_F, x]               ; x << 'Fizz'
  in [*, NOT_B, NOT_B, NOT_B, NOT_B, x] ; x << 'Buzz'
  in [*, x]                             ; x << a.size.to_s
  end
  puts a.last
  fizzbuzz(a)
end

fizzbuzz([])

正規表現使わなくても、こんな感じで書ける!長いけど。

def fizzbuzz(a)
  buffer = ''
  case a
  in [] | [*, :_0] | [[*, :_0]]
    a << :_1
  in [*, :_1] | [[*, :_1]]
    a << :_2
  in [*, :_2] | [[*, :_2]]
    a << :_0
    buffer << 'Fizz'
  end

  case a
  in [[*], _, _, _, _]
  in [_, _, _, _, _] | [[*], _, _, _, _, _]
    a = [a]
    buffer << 'Buzz'
  else
  end

  buffer << a.flatten.size.to_s if buffer.empty?
  puts(buffer)
  fizzbuzz(a)
end

fizzbuzz([])

# []
# [1]
# [1, 2]
# [1, 2, 0]
# [1, 2, 0, 1]
# [[1, 2, 0, 1, 2]]
# [[1, 2, 0, 1, 2], 0]
# [[1, 2, 0, 1, 2], 0, 1]
# [[1, 2, 0, 1, 2], 0, 1, 2]
# [[1, 2, 0, 1, 2], 0, 1, 2, 0]
# [[[1, 2, 0, 1, 2], 0, 1, 2, 0, 1]]
# [[[1, 2, 0, 1, 2], 0, 1, 2, 0, 1], 2]
# [[[1, 2, 0, 1, 2], 0, 1, 2, 0, 1], 2, 0]
# [[[1, 2, 0, 1, 2], 0, 1, 2, 0, 1], 2, 0, 1]
# [[[1, 2, 0, 1, 2], 0, 1, 2, 0, 1], 2, 0, 1, 2]
# [[[[1, 2, 0, 1, 2], 0, 1, 2, 0, 1], 2, 0, 1, 2, 0]]

もっとうまく書ける気もする。

使ってるバージョンは下記の通り。

$ ruby -v
ruby 2.7.0dev (2019-05-25 trunk 65ce14e7b5) [x86_64-darwin17]

その他、パターンマッチングが嬉しいときの例

木構造を扱うときに嬉しいみたい。

✍クイックソート

メドピアさんのブログの例がよくて

def qsort(ary) 
  case ary
  in [piv, *xs] # 要素が2個以上
    lt, rt = xs.partition{|x| x < piv}
    qsort(lt) + [piv] + qsort(rt)
  else # 要素が0個または1個
    ary
  end
end

✍赤黒木

このRubyKaigiのトークの11:12あたり

Author

hoshinotsuyoshi

星野剛志(ほしのつよし) web application engineer. ruby/rails/docker