2016-12-15 13 views
1

私は以下の問題を解決しようとしていて、エラーに遭遇しました。問題のポイントは、指定されたキーシーケンスを使用して文字列を暗号化することです。たとえば、 "cat"と[1,2,3]と指定すると結果は "dcw"になるはずです 提案はありますか?エラーではなく、あなたが主な目的で定義されている、あなたがStringクラスに定義されていない文字列オブジェクトにshift!を呼び出そうとしている以下の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'は文字列(1文字)です。メソッド '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

のようにそれを呼び出すことができます。

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 

は、それがkey_sequence.lengthkeyIndexを割り当てます。 Rubyでは数字が真実だから、ifステートメント内のコードを実行します。

if keyIndex==key_sequence.length 

ステップ5

あなたのコードは[nil, nil, 0]を返すと交換してください。どうして?

は、mapの結果として定義される。 mapは、各要素がブロック内で最後に実行されたコマンドの結果である配列を返します。この場合、ifステートメントです。

ifは、条件が満たされない場合にnilを返し、そうでない場合は最後に実行されたコマンドを返します。この場合0

mapブロックの最後の行にcを追加します。

ステップ6

あなたのコードは今["c", "b", "v"]を返します。どうして?

key_sequenceアレイで定義された額ではなく、shiftIndexだけシフトします。あなたのコードは["d", "c", "w"]を返し

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

ステップ7

c=shift!(c,keyIndex) 

を交換してください。ほぼそこに!

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 

ステップ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]) 

は、スペースの、いずれかの動作しません。 z後には文字はありませんので、

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

ステップ10

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

が空の文字列を返します。文字ではないものを削除します。

使用法と26:11

が不要returnを削除し、あなたが得る、変数のためのキャメルケースを使用しない、タイプミスを削除

return alphabet[finalLetterIndex%26] 

ステップ:

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 ND 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 

ステップ13

をウィキペディアarticleはキーとして文字列を使用しています:

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に関して、「オフセット」が1の「z」はどこにありますか? vigenere_cipher( "zzz"、[1,2,3]) 'は無効であると主張します.vigenere_cipher(" zzz "、[-25、-24、 - 、23])# > 'abc'は必要なマッピングを生成するために必要です。 –

+0

@CarySwoveland:歴史的な理由がほとんどです.Wikipediaの記事を参照してください:https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipherこの暗号は、 ASCIIテーブル。すべての演算はモジュロ26で行われる。 –

+0

"Vigenere"を知らないので、メソッド名がOPの発明であると仮定した。当然、私は暗号がOPの意図であると思ったが、 n質問の文字通りの解釈。 –

関連する問題