2016-12-15 76 views
1

我一直在努力解決以下問題,並遇到錯誤。問題的關鍵是使用給定的密鑰序列來加密字符串。例如,當給出「貓」和[1,2,3]時,結果應該是「dcw」 有什麼建議嗎?錯誤是以下私人方法調用noMethodError ruby​​

def vigenere_cipher(string, key_sequence) 
    keyIndex=0 
    string=string.each_char.map do |c| 
    c=c.shift!(c,keyIndex) 
    keyIndex+=1 
    if keyIndex=key_sequence.length 
     keyIndex=0 
    end 
    end 
    return string 
end 

def shift!(c,keyIndex) 
    alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] 
    inititalLetterIndex=alphabet.index(c) 
    finalLetterIndex=alphabet[inititalLetterIndex+keyIndex] 
    return alphabet[finalLetterIndex] 
end 

vigenere_cipher("cat", [1,2,3]) 
# private method `shift!' called for "c":String (NoMethodError) 
+0

'c'是一個字符串(單個字符)。沒有方法'String#shift!'。 –

+0

嘗試''cat'.chars.zip([1,2,3])。map {| c,i | (c.ord + i).chr} .join#=>「dcw」'。 –

+0

但我定義了這個轉變!方法...抱歉,我是新手。爲什麼我定義的班次!方法不起作用? –

回答

3

您正在試圖調用shift!字符串對象不上String類中定義的,而不是你的主要對象定義。你可以這樣調用它shift!(c,keyIndex)而不是c.shift!(c,keyIndex)

1

如果你想給你打電話的方法shift!上的繩子,你必須定義它String類。

class String 
    def shift!(keyIndex) 
    # you can access `c` using `self` here 
    ... 
    end 
end 

然後你可以把它叫做c.shift!(keyIndex)(注意參數不同)。

1

步驟1

cipher.rb:4:in `block in vigenere_cipher': private method `shift!' called for "c":String (NoMethodError) 

shift!未在String類定義的,但在頂層。 因此,通過c=shift!(c,keyIndex)

步驟2

cipher.rb:17:in `[]': no implicit conversion of String into Integer (TypeError) 

線16限定替換c=c.shift!(c,keyIndex)

finalLetterIndex=alphabet[inititalLetterIndex+keyIndex] 

字母表包含字母字符串,所以finalLetterIndex不是索引(數字),但一個字符串。

在第17行,您嘗試使用此字符串作爲索引。

替換線16:

finalLetterIndex=inititalLetterIndex+keyIndex 

步驟3

您的腳本不會引發任何異常了。它也沒有顯示任何東西,所以加一個放至最後一行:

puts vigenere_cipher("cat", [1,2,3]).inspect 

它返回:

[0, 0, 0] 

步驟4

keyIndex似乎爲0。爲什麼要堅持? 看行6:

if keyIndex=key_sequence.length 

它不檢查的平等,它分配給keyIndexkey_sequence.length。由於任何數字在Ruby中都是真的,它會執行if語句中的代碼。與

if keyIndex==key_sequence.length 

步驟5

你的代碼返回[nil, nil, 0]更換。爲什麼?

string定義爲map的結果。 map返回一個數組,其中每個元素都是塊內最後執行的命令的結果:在這種情況下,爲if語句。當條件不滿足時返回,否則返回最後執行的命令。在這種情況下,0

map塊的最後一行添加c

步驟6

現在您的代碼返回["c", "b", "v"]。爲什麼?

您只能移動shiftIndex,而不是key_sequence Array中定義的數量。更換

c=shift!(c,keyIndex) 

c=shift!(c,key_sequence[keyIndex]) 

步驟7

你的代碼返回["d", "c", "w"]。差不多了!

Ruby是一種動態語言。你可以用Array來自由地覆蓋字符串string,但它會混淆他人和你未來的自我。

使用arrayletters代替string,並返回letters.join

你的腳本現在返回"dcw"

它應該看起來像:

def vigenere_cipher(string, key_sequence) 
    keyIndex=0 
    letters=string.each_char.map do |c| 
    c=shift!(c,key_sequence[keyIndex]) 
    keyIndex+=1 
    if keyIndex==key_sequence.length 
     keyIndex=0 
    end 
    c 
    end 
    return letters.join 
end 

def shift!(c,keyIndex) 
    alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] 
    inititalLetterIndex=alphabet.index(c) 
    finalLetterIndex=inititalLetterIndex+keyIndex 
    return alphabet[finalLetterIndex] 
end 

步驟8

vigenere_cipher("Hello", [1,2,3])

提高

cipher.rb:17:in 'shift!': undefined method '+' for nil:NilClass (NoMethodError)

嗯,在您的字母表中找不到'H'。使用downcase:

array=string.downcase.each_char.map do |c| 

步驟9

vigenere_cipher("Hello World", [1,2,3]) 

也不管用,因爲空間。刪除任何不信:

array=string.downcase.delete('^a-z').each_char.map do |c| 

步驟10

vigenere_cipher("zzz", [1,2,3]) 

返回一個空字符串,因爲有z後不信。

使用模26:

return alphabet[finalLetterIndex%26] 

步驟11

刪除錯別字,不要使用變量駝峯,消除不必要的return,你會得到:

def vigenere_cipher(string, key_sequence) 
    key_index = 0 
    letters = string.downcase.delete('^a-z').each_char.map do |c| 
    c = shift(c, key_sequence[key_index]) 
    key_index = (key_index + 1) % key_sequence.length 
    c 
    end 
    letters.join 
end 

def shift(c, key_index) 
    alphabet = ('a'..'z').to_a 
    initial_letter_index = alphabet.index(c) 
    final_letter_index = initial_letter_index + key_index 
    alphabet[final_letter_index % 26] 
end 

步驟12

使用each_charzip a

class Integer 
    # 0 => 'a', 1 => 'b', ..., 25 => 'z', 26 => 'a' 
    def to_letter 
    ('a'.ord + self % 26).chr 
    end 
end 

class String 
    # 'A' => '0', 'a' => 0, ..., 'z' => 25 
    def to_code 
    self.downcase.ord - 'a'.ord 
    end 
end 

def vigenere_cipher(string, key) 
    short_string = string.delete('^A-Za-z') 
    short_string.each_char.zip(key.cycle).map do |char, shift| 
    (char.to_code + shift).to_letter 
    end.join 
end 

步驟13

維基百科article使用String作爲重點:第二cycle,我想這種方式重寫整個代碼

def vigenere_cipher(string, key) 
    short_string = string.delete('^A-Za-z') 
    short_string.each_char.zip(key.each_char.cycle).map do |char, shift| 
    (char.to_code + shift.to_code).to_letter 
    end.join 
end 

vigenere_cipher('Attack at dawn!', 'LEMON').upcase # => "LXFOPVEFRNHR" 

步驟14

你也應該能夠解密該消息:

def vigenere_cipher(string, key, decrypt = false) 
    short_string = string.delete('^A-Za-z') 
    short_string.each_char.zip(key.each_char.cycle).map do |char, shift| 
    (char.to_code + shift.to_code * (decrypt ? -1 : 1)).to_letter 
    end.join 
end 

vigenere_cipher("LXFOPVEFRNHR", 'LEMON', :decrypt) #=> "attackatdawn" 

好吧,那比預期的要長! :D

+0

關於步驟10,它在哪裏說'z'的偏移量爲1映射到'a'?我會爭辯說vigenere_cipher(「zzz」,[1,2,3])'無效,'vigenere_cipher(「zzz」,[-25,-24, - ,23])# >'abc''is需要產生所需的映射。 –

+0

@CarySwoveland:主要是出於歷史原因。參見維基百科文章:https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher。這個密碼早在ASCII表格,每個操作都以模26完成。 –

+0

由於沒有意識到「Vigenere」,我認爲方法的名稱是OP的發明,當然,我懷疑密碼是OP的意圖,但我打算o n對問題的字面解釋。 –

相關問題