13
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

case文でProc#curryを使うと便利かもしれない

Last updated at Posted at 2014-10-22

RubyのProcクラスはとても便利なのですが、最近このクラスにcurryというメソッドがあるのを知りました。

a = -> (x, y, z) {(x||0) + (y||0) + (z||0) }
p a.curry[1][2][3]
#=> 6

例えば「1からnの連続した数字を列挙する。ただし、3で割れるならFizz、5で割れるならBuzzと数字の代わりに表示する」という動作をさせたいとします

実装1.rb
def fizzbuzz(num)
  (1..num).map do |n|
    case
    when n % 3 == 0 then 'Fizz'
    when n % 5 == 0 then 'Buzz'
    else n
    end
  end.join(' ')
end

# 普通はこんなふうには書かないが、あえてProcを使う
def fizzbuzz_v2(num)
  (1..num).map do |n|
    factor = ->(y){ n % y == 0 }
    if factor === 3 # factor[3] や factor.call(3)と同じ
      'Fizz'
    elsif factor === 5
      'Buzz'
    else
      n
    end
  end.join(' ')
end

puts fizzbuzz 30
#=> "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 Fizz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 Fizz
puts fizzbuzz_v2 30
#=> "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 Fizz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 Fizz

これは Proc#curryで書くと少しだけDRYになります。

実装2.rb
def fizzbuzz(num)
  # factor[y][x]で、yがxで割り切れるかの判定をtrue/falseで返す
  factor = ->(y, x){ x % y == 0 }.curry

  (1..num).map do |n|
    case n
    when factor[3] then 'Fizz' # factor[3][n] の判定
    when factor[5] then 'Buzz'
    else n
    end
  end.join(' ')
end

puts fizzbuzz 30
#=> "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 Fizz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 Fizz"

factorの中身がもう少し複雑になると、caseで同じような条件をいくつか判定する際に有り難みが出てきそうな気がしますね。

リファレンスを見てみると、可変長引数の場合は以下のような使い方も出来るよということです

# 引数を合算する ex) sum[1,20,300] #=> 321
sum = ->(*args){args.inject(:+)}

sum_c = sum.curry
sum_c[1][2][3]
# 最初のsum_c[1]で数値が帰ってくるので、意図した動きにならない

# 可変長引数の長さを3と固定してしまう
sum_c3 = sum.curry(3)
sum_c3[1][2] #=> <Proc:0x007f0a484f5958 (lambda)>
             #引数を2つしかもらってないので、まだProcオブジェクト
sum_c3[1][2][3] #=> 6

# こういう書き方でもOK.
sum_c3[1,2][3] # caseで使うときは、この書き方を使えそう?
sum_c3[1][2,3]
sum_c3[1,2,3]

今回は->を使いましたがlambda proc Proc.newあたりでも同じように使えます。あんまり使い分けたことがない…

参考

13
12
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?