是否可以保持非字母符号完好无损?
Is it possible to keep the non letter symbols intact?
我是 Ruby 初学者,我正在开发一个密码程序。
它接受一个短语,将字符串转换为数字,用给定值递增数字,然后再次将它们转换为字符串。
我想知道如何保持非字母符号不变。例如“!”或 space。
我写的代码如下:
def caesar_cypher ( phrase, number=0)
letters = phrase.downcase.split(//)
letters_to_numbers= letters.map { |idx| idx.ord }
incrementor = letters_to_numbers.map { |idx| idx+number}
numbers_to_letters = incrementor.map { |idx| idx.chr}.join.capitalize
p numbers_to_letters
#binding.pry
end
caesar_cypher("Hello world!", 4)
caesar_cypher("What a string!", 6)
使用 Array#rotate 和 Hash#fetch 的解决方案
是的,您可以不加修改地传递字符,但为此您需要定义什么是“字母”以及您希望在#map 块中的编码中包含或排除的内容。这是处理执行这些操作的问题的一种略有不同的方法,但也更短并且增加了一些额外的灵活性。
- 创建一个包含英文字母表中所有大写和小写字符的数组,并使用 Array#rotate 的倒置哈希值为每个字符分配一个替换值,其中旋转值是您的可重现密码密钥。
- 当您没有加密值时发出警告,因为旋转是
key % 26 == 0
,但无论如何都允许它。这有助于测试。否则,如果您不想允许明文结果,您可以简单地引发异常,或者为 key. 设置默认值
- 不要大写你的句子。这限制了您的随机性,并阻止您为大写字母设置单独的值。
- 使用带有 Hash#fetch 的默认值允许您 return 任何不在您的 Hash 中的字符而不对其进行编码,因此 UTF-8 或标点符号将简单地按原样传递.
- 空格不是散列中定义的编码的一部分,因此您可以使用 String#join 而无需特殊对待它们。
使用 Ruby 3.0.2:
def caesar_cypher phrase, key
warn "no encoding when key=#{key}" if (key % 26).zero?
letters = [*(?A..?Z), *(?a..?z)]
encoding = letters.rotate(key).zip(letters).to_h.invert
phrase.chars.map { encoding.fetch _1, _1 }.join
end
您可以通过以下一些示例验证这是否为您提供了可重复的输出:
# verify your mapping with key=0,
# which returns the phrase as-is
caesar_cypher "foo bar", 0
#=> "foo bar"
caesar_cypher "foo bar", 5
#=> "ktt gfw"
caesar_cypher "Et tu, Brute?", 43
#=> "vk kl, silkV?"
# use any other rotation value you like;
# you aren't limited to just 0..51
caesar_cypher "Veni, vidi, vici", 152
#=> "Raje, reZe, reYe"
# UTF-8 and punctuation (actually, anything
# not /[A-Za-z]/) will simply pass through
# unencoded since it's not defined in the
# +encoding+ Hash
caesar_cypher "î.ô.ú.", 10
#=> "î.ô.ú."
编号参数的语法说明
上面的代码应该适用于最新的 Ruby 版本,但在早于 2.7 的版本上,您可能需要将块中的 _1
变量替换为类似以下内容的内容:
phrase.chars.map { |char| encoding.fetch(char, char) }.join
而不是依赖编号的位置参数。我想不出还有什么可以阻止此代码在任何未过期的 Ruby 版本上 运行,但是如果您发现一些具体的内容,请添加评论。
A_ORD = 'A'.ord
def caesar_cypher(str, offset)
h = ('A'..'Z').each_with_object(Hash.new(&:last)) do |ch,h|
h[ch] = (A_ORD + (ch.ord - A_ORD + offset) % 26).chr
h[ch.downcase] = (h[ch].ord + 32).chr
end
str.gsub(/./, h)
end
试试吧。
caesar_cypher("Hello world!", 4)
#=> "Lipps asvph!"
caesar_cypher("What a string!", 6)
#=> "Cngz g yzxotm!"
在执行第一个示例时,变量 h
持有的散列值等于
{"A"=>"E", "a"=>"e", "B"=>"F", "b"=>"f", "C"=>"G", "c"=>"g", "D"=>"H",
"d"=>"h", "E"=>"I", "e"=>"i", "F"=>"J", "f"=>"j", "G"=>"K", "g"=>"k",
"H"=>"L", "h"=>"l", "I"=>"M", "i"=>"m", "J"=>"N", "j"=>"n", "K"=>"O",
"k"=>"o", "L"=>"P", "l"=>"p", "M"=>"Q", "m"=>"q", "N"=>"R", "n"=>"r",
"O"=>"S", "o"=>"s", "P"=>"T", "p"=>"t", "Q"=>"U", "q"=>"u", "R"=>"V",
"r"=>"v", "S"=>"W", "s"=>"w", "T"=>"X", "t"=>"x", "U"=>"Y", "u"=>"y",
"V"=>"Z", "v"=>"z", "W"=>"A", "w"=>"a", "X"=>"B", "x"=>"b", "Y"=>"C",
"y"=>"c", "Z"=>"D", "z"=>"d"}
片段
Hash.new(&:last)
如果与
相同
Hash.new { |h,k| k }
其中块变量 h
是正在创建的(初始为空)散列,k
是一个键。如果定义了哈希
hash = Hash.new { |h,k| k }
then(可能在添加键值对之后)如果hash
没有键k
,hash[k]
returnsk
(即, 字符 k
保持不变).
参见 Hash::new 的形式,它采用块但没有参数。
我们可以轻松创建解密方法。
def caesar_decrypt(str, offset)
caesar_cypher(str, 26-offset)
end
offset = 4
s = caesar_cypher("Hello world!", offset)
#=> "Lipps asvph!"
caesar_decrypt(s, offset)
#=> "Hello world!"
offset = 24
s = caesar_cypher("Hello world!", offset)
#=> Fcjjm umpjb!
caesar_decrypt(s, offset)
#=> "Hello world!"
我是 Ruby 初学者,我正在开发一个密码程序。 它接受一个短语,将字符串转换为数字,用给定值递增数字,然后再次将它们转换为字符串。 我想知道如何保持非字母符号不变。例如“!”或 space。 我写的代码如下:
def caesar_cypher ( phrase, number=0)
letters = phrase.downcase.split(//)
letters_to_numbers= letters.map { |idx| idx.ord }
incrementor = letters_to_numbers.map { |idx| idx+number}
numbers_to_letters = incrementor.map { |idx| idx.chr}.join.capitalize
p numbers_to_letters
#binding.pry
end
caesar_cypher("Hello world!", 4)
caesar_cypher("What a string!", 6)
使用 Array#rotate 和 Hash#fetch 的解决方案
是的,您可以不加修改地传递字符,但为此您需要定义什么是“字母”以及您希望在#map 块中的编码中包含或排除的内容。这是处理执行这些操作的问题的一种略有不同的方法,但也更短并且增加了一些额外的灵活性。
- 创建一个包含英文字母表中所有大写和小写字符的数组,并使用 Array#rotate 的倒置哈希值为每个字符分配一个替换值,其中旋转值是您的可重现密码密钥。
- 当您没有加密值时发出警告,因为旋转是
key % 26 == 0
,但无论如何都允许它。这有助于测试。否则,如果您不想允许明文结果,您可以简单地引发异常,或者为 key. 设置默认值
- 不要大写你的句子。这限制了您的随机性,并阻止您为大写字母设置单独的值。
- 使用带有 Hash#fetch 的默认值允许您 return 任何不在您的 Hash 中的字符而不对其进行编码,因此 UTF-8 或标点符号将简单地按原样传递.
- 空格不是散列中定义的编码的一部分,因此您可以使用 String#join 而无需特殊对待它们。
使用 Ruby 3.0.2:
def caesar_cypher phrase, key
warn "no encoding when key=#{key}" if (key % 26).zero?
letters = [*(?A..?Z), *(?a..?z)]
encoding = letters.rotate(key).zip(letters).to_h.invert
phrase.chars.map { encoding.fetch _1, _1 }.join
end
您可以通过以下一些示例验证这是否为您提供了可重复的输出:
# verify your mapping with key=0,
# which returns the phrase as-is
caesar_cypher "foo bar", 0
#=> "foo bar"
caesar_cypher "foo bar", 5
#=> "ktt gfw"
caesar_cypher "Et tu, Brute?", 43
#=> "vk kl, silkV?"
# use any other rotation value you like;
# you aren't limited to just 0..51
caesar_cypher "Veni, vidi, vici", 152
#=> "Raje, reZe, reYe"
# UTF-8 and punctuation (actually, anything
# not /[A-Za-z]/) will simply pass through
# unencoded since it's not defined in the
# +encoding+ Hash
caesar_cypher "î.ô.ú.", 10
#=> "î.ô.ú."
编号参数的语法说明
上面的代码应该适用于最新的 Ruby 版本,但在早于 2.7 的版本上,您可能需要将块中的 _1
变量替换为类似以下内容的内容:
phrase.chars.map { |char| encoding.fetch(char, char) }.join
而不是依赖编号的位置参数。我想不出还有什么可以阻止此代码在任何未过期的 Ruby 版本上 运行,但是如果您发现一些具体的内容,请添加评论。
A_ORD = 'A'.ord
def caesar_cypher(str, offset)
h = ('A'..'Z').each_with_object(Hash.new(&:last)) do |ch,h|
h[ch] = (A_ORD + (ch.ord - A_ORD + offset) % 26).chr
h[ch.downcase] = (h[ch].ord + 32).chr
end
str.gsub(/./, h)
end
试试吧。
caesar_cypher("Hello world!", 4)
#=> "Lipps asvph!"
caesar_cypher("What a string!", 6)
#=> "Cngz g yzxotm!"
在执行第一个示例时,变量 h
持有的散列值等于
{"A"=>"E", "a"=>"e", "B"=>"F", "b"=>"f", "C"=>"G", "c"=>"g", "D"=>"H",
"d"=>"h", "E"=>"I", "e"=>"i", "F"=>"J", "f"=>"j", "G"=>"K", "g"=>"k",
"H"=>"L", "h"=>"l", "I"=>"M", "i"=>"m", "J"=>"N", "j"=>"n", "K"=>"O",
"k"=>"o", "L"=>"P", "l"=>"p", "M"=>"Q", "m"=>"q", "N"=>"R", "n"=>"r",
"O"=>"S", "o"=>"s", "P"=>"T", "p"=>"t", "Q"=>"U", "q"=>"u", "R"=>"V",
"r"=>"v", "S"=>"W", "s"=>"w", "T"=>"X", "t"=>"x", "U"=>"Y", "u"=>"y",
"V"=>"Z", "v"=>"z", "W"=>"A", "w"=>"a", "X"=>"B", "x"=>"b", "Y"=>"C",
"y"=>"c", "Z"=>"D", "z"=>"d"}
片段
Hash.new(&:last)
如果与
相同Hash.new { |h,k| k }
其中块变量 h
是正在创建的(初始为空)散列,k
是一个键。如果定义了哈希
hash = Hash.new { |h,k| k }
then(可能在添加键值对之后)如果hash
没有键k
,hash[k]
returnsk
(即, 字符 k
保持不变).
参见 Hash::new 的形式,它采用块但没有参数。
我们可以轻松创建解密方法。
def caesar_decrypt(str, offset)
caesar_cypher(str, 26-offset)
end
offset = 4
s = caesar_cypher("Hello world!", offset)
#=> "Lipps asvph!"
caesar_decrypt(s, offset)
#=> "Hello world!"
offset = 24
s = caesar_cypher("Hello world!", offset)
#=> Fcjjm umpjb!
caesar_decrypt(s, offset)
#=> "Hello world!"