【问题标题】:How do I iterate through an array and change characters efficiently?如何遍历数组并有效地更改字符?
【发布时间】:2026-02-17 01:55:02
【问题描述】:

我目前在我的 Ruby 作业中使用这种方法,我需要将用户输入的名称中的元音和辅音更改为下一个连续的元音/辅音,所以 ( a = e, e = i等)和辅音的相同过程(b = c,d = f 等)。我以“简单”的方式完成了它,但现在我需要更改它,以便我遍历数组并以这种方式更改元音/辅音。

我是迭代新手,所以我遇到了麻烦。

这是我原来的方法:

puts "What is your full name?"
full_name = gets.chomp 

def vowelreplace(full_name)
 vowels = 'aeiou'
 replacements = 'eioua'
 full_name.tr(vowels, replacements)
end

name_next_vowel = vowelreplace(full_name)

p name_next_vowel

def consonantreplace(name_next_vowel)
  consonants = 'bcdfghjklmnpqrstvwxyz'
  replacements = 'cdfghjklmnpqrstvwxyzb'
  name_next_vowel.tr(consonants, replacements)
end

new_spyname = consonantreplace(name_next_vowel)

p new_spyname

下面是我开始使用数组和块方法更改它的方法。有没有更简单、更省时的方法来做到这一点?有没有一种 .next 方法可以做到这一点,或者一般来说,一种更有效的方法?

puts "What is your first name?"
first_name = gets.chomp 

arr = first_name.split("") 

p arr 

arr.map! { |element|
  if(element == "a")
    "e" 
  elsif(element == "e")
    "i"
  elsif(element == "i")
    "o"
  elsif(element == "o")
    "u"
  elsif(element == "u")
    "a"
  else
    element
  end
 }

p arr

new_arr = arr

new_arr.map! { |element|
  if(element == "b")
    "c" 
  elsif(element == "c")
    "d"
  elsif(element == "d")
    "f"
  elsif(element == "f")
    "g"
  elsif(element == "g")
    "h"
  elsif(element == "h")
    "j"
  elsif(element == "j")
    "k"
  elsif(element == "k")
    "l"
  elsif(element == "l")
    "m"
  elsif(element == "m")
    "n"
  elsif(element == "n")
    "p"
  elsif(element == "p")
    "q"
  elsif(element == "q")
    "r"
  elsif(element == "r")
    "s"
  elsif(element == "s")
    "t"
  elsif(element == "t")
    "v"
  elsif(element == "v")
    "w"
  elsif(element == "w")
    "x"
  elsif(element == "x")
    "y"
  elsif(element == "y")
    "z"
  elsif(element == "z")
    "b"
  else
     element
  end
 }

p new_arr 

arr.join("")

【问题讨论】:

  • 由于这是一项任务,我不愿给出完整的解决方案。首先,您可以像最初那样轻松地使用tr 方法,但是对map(迭代器)块中的每个字符都这样做。另一种可能性是在原始字符和它们应该转换成的字符之间创建一个映射。 "a" => "e", "b" => "c" 等等。这只是哈希表示法!您可以轻松地查找每个字符的下一个值。

标签: arrays ruby loops iteration


【解决方案1】:

您可以像使用字符串一样将String#tr 与字符数组一起使用,但是tr 的时间复杂度为 O(n),因为在尝试查找匹配项时会按顺序检查第一个参数的字符。如果要进行大量这样的查找,那么使用散列会快得多,散列的查找时间几乎是恒定的。你可以写一个方法如下。

代码

VOWELS = %w|a e i o u|
  #=> ["a", "e", "i", "o", "u"]
NEXT_VOWEL = VOWELS.zip(VOWELS.rotate(1)).to_h
  #=> {"a"=>"e", "e"=>"i", "i"=>"o", "o"=>"u", "u"=>"a"}
CONSONANTS = ('a'..'z').to_a - VOWELS
  #=> ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n",
  #    "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"]
NEXT_CONSONANT = CONSONANTS.zip(CONSONANTS.rotate(1)).to_h
  #=> {"b"=>"c", "c"=>"d", "d"=>"f", "f"=>"g", "g"=>"h", "h"=>"j", "j"=>"k",
  #    "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"p", "p"=>"q", "q"=>"r", "r"=>"s",
  #    "s"=>"t", "t"=>"v", "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"b"}

def code(arr)
  arr.map { |c| NEXT_CONSONANT[c] || NEXT_VOWEL[c] || c }
end

大概,您也需要能够解码。您可以编写如下解码方法,使用方法Hash#invert

PREVIOUS_VOWEL = NEXT_VOWEL.invert
  # => {"e"=>"a", "i"=>"e", "o"=>"i", "u"=>"o", "a"=>"u"}
PREVIOUS_CONSONANT = NEXT_CONSONANT.invert
  #=> {"c"=>"b", "d"=>"c", "f"=>"d", "g"=>"f", "h"=>"g", "j"=>"h", "k"=>"j", "l"=>"k",
  #    "m"=>"l", "n"=>"m", "p"=>"n", "q"=>"p", "r"=>"q", "s"=>"r", "t"=>"s", "v"=>"t",
  #    "w"=>"v", "x"=>"w", "y"=>"x", "z"=>"y", "b"=>"z"}

def decode(arr)
  arr.map { |c| PREVIOUS_CONSONANT[c] || PREVIOUS_VOWEL[c] || c }
end

示例

首先对数组进行编码。

arr = "billy-bob".chars
  #=> ["b", "i", "l", "l", "y", "-", "b", "o", "b"]
a = code(arr)
  #=> ["c", "o", "m", "m", "z", "-", "c", "u", "c"]
a.join
  #=> "commz-cuc"

然后解码。

a = decode ["c", "o", "m", "m", "z", "-", "c", "u", "c"]
  #=> ["b", "i", "l", "l", "y", "-", "b", "o", "b"]
a.join
  #=> "billy-bob"

说明

考虑表达式

NEXT_CONSONANT[c] || NEXT_VOWEL[c] || c

在方法code中。假设c = 'd'。然后NEXT_COSONANT['d'] #=> 'f',所以我们有

'f' || NEXT_VOWEL[c] || c

由于'f' 是真实的,'f' 被返回。 Ruby 没有理由评估NEXT_VOWEL[c] || c,所以她没有。

现在假设c = 'e'。那么,由于NEXT_CONSONANT没有键'e'NEXT_CONSONANT['e'] #=> nil,表达式变为

nil || NEXT_VOWEL[c] || c

因此有必要评估NEXT_VOWEL['e']。因为返回'i',这是真的,所以'i'被返回,不需要评估c

最后,如果c = '-',我们得到

nil || nil || c

所以c 被评估并返回。

【讨论】:

    【解决方案2】:

    我喜欢tr 的方法,但如果你想要类似于next 的东西,那么Array#rotate 可能是一个不错的选择;这是一个例子:

    def letter_swap(full_name)
      vowels     = %w(a e i o u)
      consonants = %w(b c d f g h j k l m n p q r s t v w x y z)
    
      full_name.each_char.with_object("") do |char, new_name|
        if vowels.include?(char)
          new_name << vowels.rotate(vowels.index(char) + 1).first
        elsif consonants.include?(char)
          new_name << consonants.rotate(consonants.index(char) + 1).first
        else
          new_name << char
        end
      end
    end
    

    当然,您可以 DRY 这段代码,但会牺牲可读性 (IMO);例如:

    def letter_swap(full_name)
      letters = [%w(a e i o u), %w(b c d f g h j k l m n p q r s t v w x y z)]
    
      full_name.each_char.with_object("") do |char, new_name|
        group = letters.find { |group| group.include?(char) }
        new_name << (group.nil? ? char : group.rotate(group.index(char) + 1).first)
      end
    end
    

    【讨论】:

    • 非常感谢!我真的很喜欢第一种方法的可读性……这很有意义。还没用过rotate的方法,太好了!
    • @BrittFlowers 很高兴为您提供帮助! :) 不要忘记通过单击左侧的勾号将答案标记为已接受(此答案或您认为最有帮助的答案)。
    • 感谢您的提醒!我是一个堆栈溢出新手,所以我很欣赏这些提示:)
    【解决方案3】:

    您也许可以在 map 循环中使用一系列 case 语句?

    arr.map! { |element|
      case element
        when "a" then "e"
        when "e" then "i"
        when "i" then "o"
        when "o" then "u"
        when "u" then "a"
      end
    }
    

    辅音也一样..

    【讨论】:

    • 非常感谢!哇,这是一个更简单的方法,太棒了!
    • 不客气!如果您觉得我的回答有帮助,我将非常感谢您的支持!