2015-02-09 154 views
0

下面是兩個相同的類,它們與運算符+<<的區別不同。這些可以在注入方法中找到。在+的情況下,測試通過,並在<<其中一些失敗。爲什麼?Ruby中+和<<之間的區別

class Integer 
    ROMAN_NUMERALS = { 
    0 => '', 
    1 => 'I', 2 => 'II', 3 => 'III', 4 => 'IV', 5 => 'V', 6 => 'VI', 7 => 'VII', 8 => 'VIII', 9 => 'IX', 
    10 => 'X', 20 => 'XX', 30 => 'XXX', 40 => 'XL', 50 => 'L', 60 => 'LX', 70 => 'LXX', 80 => 'LXXX', 90 => 'XC', 
    100 => 'C', 200 => 'CC', 300 => 'CCC', 400 => 'CD', 500 => 'D', 600 => 'DC', 700 => 'DCC', 800 => 'DCCC', 900 => 'CM', 
    1000 => 'M', 2000 => 'MM', 3000 => 'MMM' 
    } 

    def to_roman 
    to_s.reverse.chars.each_with_index.inject("") do |roman_numeral, (character, index)| 
     ROMAN_NUMERALS[character.to_i * 10 ** index] << roman_numeral 
    end 
    end 
end 

我得到不同的結果,當我運行

class Integer 
    ROMAN_NUMERALS = { 
    0 => '', 
    1 => 'I', 2 => 'II', 3 => 'III', 4 => 'IV', 5 => 'V', 6 => 'VI', 7 => 'VII', 8 => 'VIII', 9 => 'IX', 
    10 => 'X', 20 => 'XX', 30 => 'XXX', 40 => 'XL', 50 => 'L', 60 => 'LX', 70 => 'LXX', 80 => 'LXXX', 90 => 'XC', 
    100 => 'C', 200 => 'CC', 300 => 'CCC', 400 => 'CD', 500 => 'D', 600 => 'DC', 700 => 'DCC', 800 => 'DCCC', 900 => 'CM', 
    1000 => 'M', 2000 => 'MM', 3000 => 'MMM' 
    } 

    def to_roman 
    to_s.reverse.chars.each_with_index.inject("") do |roman_numeral, (character, index)| 
     ROMAN_NUMERALS[character.to_i * 10 ** index] + roman_numeral 
    end 
    end 
end 

我使用的測試是低於

require 'minitest/autorun' 
require_relative 'roman' 

class RomanTest < MiniTest::Unit::TestCase 
    def test_1 
    assert_equal 'I', 1.to_roman 
    end 

    def test_2 
    assert_equal 'II', 2.to_roman 
    end 

    def test_3 
    assert_equal 'III', 3.to_roman 
    end 

    def test_4 
    assert_equal 'IV', 4.to_roman 
    end 

    def test_5 
    assert_equal 'V', 5.to_roman 
    end 

    def test_6 
    assert_equal 'VI', 6.to_roman 
    end 

    def test_9 
    assert_equal 'IX', 9.to_roman 
    end 

    def test_27 
    assert_equal 'XXVII', 27.to_roman 
    end 

    def test_48 
    assert_equal 'XLVIII', 48.to_roman 
    end 

    def test_59 
    assert_equal 'LIX', 59.to_roman 
    end 

    def test_93 
    assert_equal 'XCIII', 93.to_roman 
    end 

    def test_141 
    assert_equal 'CXLI', 141.to_roman 
    end 

    def test_163 
    assert_equal 'CLXIII', 163.to_roman 
    end 

    def test_402 
    assert_equal 'CDII', 402.to_roman 
    end 

    def test_575 
    assert_equal 'DLXXV', 575.to_roman 
    end 

    def test_911 
    assert_equal 'CMXI', 911.to_roman 
    end 

    def test_1024 
    assert_equal 'MXXIV', 1024.to_roman 
    end 

    def test_3000 
    assert_equal 'MMM', 3000.to_roman 
    end 
end 

看規格在一種情況下,但不是在其他的怎麼失敗的。我認爲這些意圖是以相同的方式工作。

+0

現在你寫了一個完全不同的問題。 – Bonifacio2 2015-02-09 14:58:58

+0

附註:這應該可能在末尾用'map.with_index' +'join'寫成。 – tokland 2015-02-09 15:43:45

+0

你看過文檔嗎?那裏明確解釋了這些差異。 – 2015-02-09 17:37:29

回答

3

這條線是一個問題...

ROMAN_NUMERALS[character.to_i * 10 ** index] << roman_numeral 

它會返回一個字符串,這是正確的ROMAN_NUMERALS鍵加roman_numeral,這是你想要的東西的價值,但它也是在不斷變化的價值ROMAN_NUMERALS哈希! shovel運算符<<更改運算符左側的字符串(這就是我們所說的變異運算符)。

所以如果你測試1001的單位1將返回「我」(這很好),然後零將返回一個空字符串但將改變爲零的值爲「我」...第二個零將返回「我「(不正確),並將零值更改爲」II「。千位上的1將返回「M」,但隨後將散列值更改爲「MII」。

2

當行ROMAN_NUMERALS[character.to_i * 10 ** index] << roman_numeral正在執行時,您將替換對應於密鑰character.to_i * 10 ** index的值與其值加上roman_numeral

+0

嗨,在我的帖子上編輯得很好,但我重新考慮了結果,不得不重新調整下半場,所以你的編輯排在風中... – SteveTurczyn 2015-02-09 15:22:37

+0

沒關係,@SteveTurczyn。 :) – Bonifacio2 2015-02-09 15:41:30

2

現在你的問題已經得到解答,我想建議一種替代方法,也是一種不同的方式來執行你的測試。這需要Ruby v1.9 +,所以我們可以依賴散列鍵的順序。

代碼

首先,反向哈希元素的順序。

RNR = Hash[ROMAN_NUMERALS.to_a.reverse] 
    #=> {3000=>"MMM", 2000=>"MM", 1000=>"M",..., 2=>"II", 1=>"I", 0=>""} 

然後:

class Integer 
    def to_roman 
    num = self 
    roman = "" 
    while num > 0 
     i,r = RNR.find { |i,r| i <= num } 
     roman << r 
     num -= i 
    end 
    roman 
    end 
end 

測試目標

我們需要測試整數值相當數量,並確保我們對正確的羅馬數字相當於測試每個整數。

RNRI = RNR.invert 
    #=> {"MMM"=>3000, "MM"=>2000, "M"=>1000,..., "II"=>2, "I"=>1, ""=>0} 

class String 
    def roman_to_integer 
    num = 0 
    roman = self 
    while roman.size > 0 
     r, i = RNRI.find { |r,m| roman =~ /^#{r}/ } 
     num += i 
     roman = roman[r.size..-1] 
    end 
    num 
    end 
end 

例子

現在讓我們來調用各種整數值都Integer#to_romanString#roman_to_integer

def check_one(i) 
    roman = i.to_roman 
    puts "#{i}.to_roman = #{roman}, #{roman}.roman_to_integer = " + 
    #{roman.roman_to_integer}" 
end 

check_one(402) # 'CDII' 
    # 402.to_roman = CDII, CDII.roman_to_integer = 402 
check_one(575) # 'DLXXV' 
    # 575.to_roman = DLXXV, DLXXV.roman_to_integer = 575 
check_one(911) # 'CMXI' 
    # 911.to_roman = CMXI, CMXI.roman_to_integer = 911 
check_one(1024) # 'MXXIV' 
    # 1024.to_roman = MXXIV, MXXIV.roman_to_integer = 1024 
check_one(3000) # 'MMM' 
    # 3000.to_roman = MMM, MMM.roman_to_integer = 3000 
這兩個目標可以通過創建羅馬數字轉換爲整數的方法來滿足

測試

所以現在在你的測試中你可以使用:

def test_all(n) 
    (1..n).each { |i| test_one(i) } 
end 

def test_one(i) 
    roman = i.to_roman 
    assert_equal(i, roman.roman_to_integer, "#{i}.to_roman=#{roman}, " + 
    "#{roman}.roman_to_integer = #{roman.roman_to_integer}") 
end 
+0

謝謝。但是我會想象這會慢一點?因爲它正在從被轉換的數字循環到零與數字的位數。雖然有趣的方法。 – 2015-02-10 08:21:37

+0

我建議你做基準測試,並以答案的形式回報。如果您以前沒有使用[Benchmark](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/benchmark/rdoc/Benchmark.html),那很簡單。例如,參見我的回答[here](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/benchmark/rdoc/Benchmark.html)。 – 2015-02-10 09:21:07

+0

看起來你的速度更快。 – 2015-02-10 10:01:27

相關問題