1

現在、次のようなルビーコードがあります。

grades.sum{|g| g.grade * g.weight}

ユーザーが維持する DSL の場合、次のようなものを実装したいと思います。

grades.sum('grade' * 'weight')

これは可能ですか?

4

3 に答える 3

6

いいえ、アプリケーション ロジックを追加するためにクラスにモンキー パッチを適用しない限り、それは不可能Stringです (そうしないでください)。しかし、これは - より良く見える - で少し遊ぶと可能ですinstance_eval:

grades.sum { grade * weight }

たとえば、gradesが配列であるとします。

module Enumerable
  def sum(&block)
    inject(0) { |acc, x| acc + x.instance_eval(&block) }
  end
end
于 2012-11-03T15:28:04.453 に答える
1

厳密に言えば、いいえ。Ruby インタープリターは文字列 "grade" と "weight" をパラメーターとして "sum" に渡す前に乗算しようとするためです。String にモンキーパッチを適用して、許可するすべての操作をオーバーライドすることで実現できますが、それはひどい考えであり、メンテナンスの悪夢になります。

とはいえ、これを達成するにはいくつかの方法があります。パラメータとして完全な文字列が必要な場合:

grades.sum('grade * weight')

次に、に渡す値のコンテキストでそのコードを使用instance_evalできevalます#sum。実装は次のようになります。

class Grade
  def weight
    0.5
  end

  def grade
    75
  end
end

class Array
  def sum(code = nil)
    if block_given?
      inject(0) {|s, v| s + yield(v) }
    else
      sum {|v| v.instance_eval code }
    end
  end
end

grades = Array.new(5, Grade.new)
puts grades.sum("weight * grade")

# Expected output: 5 * 75 * 0.5 = 187.5
#
# Actual output:
# % ruby grades.rb
# 187.5

これにより、元のブロック形式も保持されます。

puts grades.sum {|g| g.weight * g.grade } # => 187.5
于 2012-11-03T15:28:42.153 に答える
0

この SO の質問Niklas B によるこのコードをご覧になることをお勧めします。

于 2012-11-03T18:13:55.327 に答える