用于插值动态变量引用的eval()的替代方案?

时间:2017-04-23 05:34:29

标签: ruby

在以下代码中:

def solve(a0, a1, a2, b0, b1, b2)
    #score index:  0 = james, 1 = sam
    score = Array.new(2, 0)
    calcScore = lambda do |x,y| 
        if ( x > y )
            score[0] += 1
        end
        if ( x < y )
            score[1] += 1       
        end
    end
    0.upto 2 do |index|
        calcScore.call(eval("a#{index}"),eval("b#{index}"))
    end
    score
end

有没有更雄辩的DRY方法来实现动态变量引用而不使用:

eval("a#{index}")

4 个答案:

答案 0 :(得分:4)

虽然local_variable_geteval似乎可以在这里完成工作,但正确的方法是:

def solve(a0, a1, a2, b0, b1, b2)
  a, b = [a0, a1, a2], [b0, b1, b2]
  # deal with score
  0.upto 2 do |index|
    calcScore.call(a[index], b[index])
  end
  score
end

或者,更好更多DRY:

def solve(*as_and_bs)
  raise unless as_and_bs.size == 6
  a, b = as_and_bs(0..2), as_and_bs(3..5)
  # deal with score
  0.upto 2 do |index|
    calcScore.call(a[index], b[index])
  end
  score
end

答案 1 :(得分:2)

使用binding.local_variable_get

 0.upto 2 do |index|
   calcScore.call(binding.local_variable_get("a#{index}"),
                  binding.local_variable_get("b#{index}"))
 end

答案 2 :(得分:1)

如果将a1,a2和a3组合成一个数组并用b做同样的事情,那么你可以使用常规[]索引:

def solve(a, b)
    #score index:  0 = james, 1 = sam
    score = Array.new(2, 0)
    calcScore = lambda do |x,y| 
        if ( x > y )
            score[0] += 1
        end
        if ( x < y )
            score[1] += 1       
        end
    end
    0.upto 2 do |index|
        calsScore.call(a[index], b[index])
    end
    score
end

您还可以为数组长度添加自定义错误检查:

raise(ArgumentError) unless [a,b].all? { |arr| arr.length == 3 }

答案 3 :(得分:1)

eval是邪恶的。不要使用它。这是一个等效代码,适用于任意数量的分数。它使用a <=> b返回-101的事实。

您的输入格式不是很方便。此代码使用each_slice并转置将[1,2,3,4,5,6]转换为[[1, 4], [2, 5], [3, 6]]。然后,您可以迭代游戏以计算总得分:

def calc_score(a, b)
  [[0, 0], [1, 0], [0, 1]][a <=> b]
end

def solve(*scores)
  size = scores.size
  raise 'Need an even number of scores' unless size.even?
  raise 'Need at least two scores' unless size > 0
  scores.each_slice(size / 2).to_a.transpose.inject([0, 0]) do |(a_total, b_total), (a, b)|
    a_score, b_score = calc_score(a, b)
    [a_total + a_score, b_total + b_score]
  end
end

甚至更短:

def solve(*scores)
  size = scores.size
  raise 'Need an even number of scores' unless size.even?
  raise 'Need at least two scores' unless size > 0
  scores.each_slice(size / 2).to_a.transpose.map do |a, b|
    calc_score(a, b)
  end.transpose.map{ |s| s.inject(:+) } # .map(&:sum) in Ruby 2.4
end

举个例子:

solve(1, 2, 3, 4, 5, 6)
# [0, 3]
solve(2, 0, 0, 3)
# [1, 1]