为什么我的 Ruby for 循环不会在我的回文方法中迭代字符串?

Why won't my Ruby for-loop iterate over a String in my palindrome method?

我在让 for 循环处理字符串时遇到问题。这只是判断一个单词是否为回文(前后拼写方式相同的单词)的一种简单方法。我已经多次调整 for 循环,但在下面不断收到相同的错误消息。谁能指出我正确的方向?

代码:

def palindrome?(string)

    string2 = ""

    for i in string
        string2 = string[i] + string2
    end 

    if string2 == string1
        return true 
    end 
end

palindrome?("abcba")

错误:

hours.rb:7:in `palindrome?': undefined method `each' for 5:Fixnum (NoMethodError)
    from hours.rb:17:in `<main>'

问题是您不能遍历字符串(就像在 Python 中那样)。您首先需要使用 .split:

将其转换为数组
for c in string.split
    string2 = c + string2
end

也就是说,您不应该在 Ruby 中使用 for 循环。它们在内部被翻译成 each 方法,因此出现了令人困惑的错误。最好从一开始就写 each:

string.split.each do |c|
    string2 = c + string2
end

没有 Ruby 程序员会在任何情况下使用 for,它只被 Ruby 的新手使用 ;-)


注意Array.each is just one iteration method; for example there's also the String.each_char方法:

string.each_char do |c|
    string2 = c + string2
end

最后,您的代码在其他几个地方都不正确。我不会向您指出所有这些错误,因为如果您自己解决这个编程练习,对您来说会更加有益和更有教育意义 ;-)

您要找的是:

def palindrome?(string)

  string2 = ""

  for i in 0...string.length
    string2 = string[i] + string2
  end

  if string2 == string
    return true
  end
end

请注意,您可以更简单地定义它:

def palindrome?(string)
  string == string.reverse
end

你可以这样写:

def palindrome?(str)
  str == str.reverse
end

正如 Carpetsmoker 所指出的,您不能直接遍历字符串。但是,Ruby 为元素提供正索引和负索引。负索引相对于数组或字符串的末尾定位。这使您可以非常有效地进行检查,并在确定没有回文后立即短路测试:

def palindrome?(str)
  (0...str.length/2).all? { |i| str[i] == str[-(i+1)] }
end

如果你想更加面向对象,可以将其转换为class中的方法 String:

class String
  def palindrome?
    (0...length/2).all? { |i| self[i] == self[-(i+1)] }
  end
end

p "abcba".palindrome?  # => true

注意 — 编辑以利用 Cary Swoveland 关于使用 all? 而不是块中明确的 return 的出色建议。这使它成为一条线。

TL;DR

除了效率低下之外,您的代码不起作用,因为 String is not an Array, nor does it mix in Enumerator 提供了 #each 方法。

虽然 String#[] 方法允许对字符串进行索引,但没有可调用的 String#each 方法。因此,您不能在 Ruby for 循环中使用 String 对象,因为它只是 #each 的语法糖。

了解异常

我不确定您 运行 的 Ruby 是哪个版本,但是您在 post 中列出的异常在我的系统上无法重现。当 运行 on Ruby 2.3.1 时,代码生成了一个相当明显的异常:

for i in string
    string2 = string[i] + string2
end 

NoMethodError: undefined method `each' for "abcba":String

这很简单。它告诉您 String 没有 #each 方法,这是 for i in string 的语法糖在幕后真正调用的。如果要迭代,则需要某种形式的 Enumerator or Enumerable 才能使用。

迭代字符串

字符串 class 有许多有用的方法可以将字符串转换为可迭代对象。一些示例包括:

例如,您的代码可以重构为使用如下块:

string = 'abcba'
tmpstr = ''
string.each_char { |char| tmpstr < char; puts true if tmpstr == 'abcba' }
#=> "abcba"

然而,虽然这强调了如何解决您的异常,但它仍然不必要地复杂且效率低下。

利用内置方法

除非你这样做是为了家庭作业,否则在 Ruby 中执行此操作的正确方法是利用以 C 速度运行的内置方法并且不要创建临时 Ruby以后需要进行垃圾回收的对象。例如,要测试给定的字符串向后或向前读取是否相同,您可以使用 String#reverse and String#eql? 方法简单地将反向字符串与原始字符串进行比较。

def palindrome? str
  str.reverse.eql? str
end

palindrome? 'abcba'
#=> true

palindrome? 'abcde'
#=> false

如果愿意,您也可以使用 String#== 而不是 #eql?,但我认为在这种情况下使用后者更清楚。方法链清楚地表明您正在调用一个 String 方法而不是一些用于比较的语言语法。在了解 Ruby 核心的来龙去脉时,这种区别可能是一个真正的帮助,但在这种情况下,无论哪种方式,结果都是相同的。