我一直在努力解决以下问题并遇到错误。问题的关键是使用给定的密钥序列来加密字符串。例如,当给出“cat”和[1,2,3]时,结果应为“dcw” 有什么建议?错误如下
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 :(得分:3)
您试图在shift!
Class上未定义的字符串对象上调用String
,而不是在主对象上定义。您可以将其称为shift!(c,keyIndex)
而不是c.shift!(c,keyIndex)
答案 1 :(得分:1)
如果您想在字符串上调用方法shift!
,则必须在String
类上定义它。
class String
def shift!(keyIndex)
# you can access `c` using `self` here
...
end
end
然后你可以把它称为c.shift!(keyIndex)
(注意参数不同)。
答案 2 :(得分:1)
cipher.rb:4:in `block in vigenere_cipher': private method `shift!' called for "c":String (NoMethodError)
shift!
未在String类中定义,但在顶级。
因此,请c=c.shift!(c,keyIndex)
c=shift!(c,keyIndex)
cipher.rb:17:in `[]': no implicit conversion of String into Integer (TypeError)
第16行定义:
finalLetterIndex=alphabet[inititalLetterIndex+keyIndex]
字母包含字母作为字符串,因此finalLetterIndex
不是索引(数字),而是字符串。
在第17行,您尝试将此String用作索引。
将第16行替换为:
finalLetterIndex=inititalLetterIndex+keyIndex
您的脚本不再引发任何异常。它也不显示任何内容,因此在最后一行添加一个puts:
puts vigenere_cipher("cat", [1,2,3]).inspect
它返回:
[0, 0, 0]
keyIndex
似乎陷入了0.为什么?
看第6行:
if keyIndex=key_sequence.length
它不测试相等性,它将keyIndex
分配给key_sequence.length
。
由于Ruby中的任何数字都是真的,它会执行if语句中的代码。替换为
if keyIndex==key_sequence.length
您的代码返回[nil, nil, 0]
。为什么呢?
string
被定义为map
的结果。 map
返回一个Array,其中每个元素都是块内最后执行的命令的结果:在这种情况下,是if
语句。
if
返回nil
,否则返回最后执行的命令。在这种情况下0
。
在c
区块的最后一行添加map
。
您的代码现在返回["c", "b", "v"]
。为什么呢?
您只需按shiftIndex
移动,而不是key_sequence
数组中定义的数量。替换
c=shift!(c,keyIndex)
与
c=shift!(c,key_sequence[keyIndex])
您的代码返回["d", "c", "w"]
。几乎就在那里!
Ruby是一种动态语言。您可以使用数组覆盖字符串string
,但这会让其他人和您未来的自我感到困惑。
使用array
或letters
代替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
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|
vigenere_cipher("Hello World", [1,2,3])
由于空间的原因,也不起作用。删除任何不是字母的内容:
array=string.downcase.delete('^a-z').each_char.map do |c|
vigenere_cipher("zzz", [1,2,3])
返回一个空字符串,因为z
之后没有字母。
使用模26:
return alphabet[finalLetterIndex%26]
删除拼写错误,不要将camelCase用于变量,删除不必要的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
使用each_char
,zip
和cycle
,我会以这种方式重写整个代码:
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
Wikipedia article使用String作为键:
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"
您还应该能够解密消息:
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