私有方法名为noMethodError ruby

时间:2016-12-15 05:38:32

标签: ruby

我一直在努力解决以下问题并遇到错误。问题的关键是使用给定的密钥序列来加密字符串。例如,当给出“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)

3 个答案:

答案 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)

第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)

第2步

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

第16行定义:

finalLetterIndex=alphabet[inititalLetterIndex+keyIndex]

字母包含字母作为字符串,因此finalLetterIndex不是索引(数字),而是字符串。

在第17行,您尝试将此String用作索引。

将第16行替换为:

finalLetterIndex=inititalLetterIndex+keyIndex

第3步

您的脚本不再引发任何异常。它也不显示任何内容,因此在最后一行添加一个puts:

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

它返回:

[0, 0, 0]

第4步

keyIndex似乎陷入了0.为什么? 看第6行:

if keyIndex=key_sequence.length

它不测试相等性,它将keyIndex分配给key_sequence.length。 由于Ruby中的任何数字都是真的,它会执行if语句中的代码。替换为

if keyIndex==key_sequence.length

第5步

您的代码返回[nil, nil, 0]。为什么呢?

string被定义为map的结果。 map返回一个Array,其中每个元素都是块内最后执行的命令的结果:在这种情况下,是if语句。

当条件不满足时,

if返回nil,否则返回最后执行的命令。在这种情况下0

c区块的最后一行添加map

第6步

您的代码现在返回["c", "b", "v"]。为什么呢?

您只需按shiftIndex移动,而不是key_sequence数组中定义的数量。替换

c=shift!(c,keyIndex)

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

第7步

您的代码返回["d", "c", "w"]。几乎就在那里!

Ruby是一种动态语言。您可以使用数组覆盖字符串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

删除拼写错误,不要将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

第12步

使用each_charzipcycle,我会以这种方式重写整个代码:

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

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"

步骤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