2017-04-23 45 views
1

在下面的代碼:用於內插動態變量引用的eval()的替代方法?

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 

是否有實現的動態變量引用一個更雄辯幹法不使用:

eval("a#{index}")

+1

binding.local_variable_get是你最好的朋友。 – Ilya

+0

so so \t 'binding.local_variable_get'是安全的eval()? –

+0

不是,它的安全局部變量獲取:) – Ilya

回答

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] 
+0

哦哇,我真的沒有想過轉置o.o 我很抱歉的廣泛的問題,我希望有一種方法來選擇多個答案。 我最初的目的是練習lambda表達式並找到插入變量引用的方法,但是您的解決方案非常有創意和優雅。榮譽。 選擇此作爲答案,鼓勵其他讀者遵循我認爲是紅寶石的方式(即使我沒有來到它)。其他答案也是正確的,所以我對廣泛的問題表示歉意。 –

+0

不錯! 'to_a'在'transpose'之前是多餘的,'inject'中'0'也是多餘的。另外一個稍微更紅寶石的方式來寫這將是'scores.each_slice(size/2).transpose.map(&calc_score).transpose.map {| s | s.inject(:+)}'。 – mudasobwa

+0

@mudasobwa:不,是的,不。謝謝! –

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 
4

雖然local_variable_geteval似乎在這裏做他們的工作,正確的AP proach是:

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 

或者,更好,更幹:

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 
+0

或散列a和b作爲鍵。 – Ilya

+0

也將是偉大的,華麗的使用'each_slice'(與初始轉換參數) – Ilya

+0

我會堅持#1,假設參數不會改變。另外,'* as_and_bs'不能防止傳遞的參數數量錯誤。在重新設計時,可以有'solve(a,b)'調用者傳遞兩個數組的地方。 –

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 }