为什么我的 Ruby for 循环不会在我的回文方法中遍历 String?

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

提问人:Brandon Hill 提问时间:8/29/2016 最后编辑:Todd A. JacobsBrandon Hill 更新时间:8/29/2016 访问量:367

问:

我在让我的 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>'
Ruby 字符串 for-loop 相等回

评论

0赞 Brandon Hill 8/29/2016
感谢您的输入!很有帮助!
0赞 Cary Swoveland 8/29/2016
你认为“中午”(而不是“中午”)是回文吗?
0赞 Todd A. Jacobs 8/30/2016
@CarySwoveland 一般来说,答案应该是“是”,在这种情况下,应该使用 #downcase 进行比较。但这并不是 OP 面临的问题的核心,这是他的 for 循环引发的例外。有时,解决边缘情况会偏离重点,这就是为什么我没有在答案中包含这个主题的原因。但是,您描述的用例肯定是生产质量应用程序中需要考虑的有效用例。

答:

3赞 Martin Tournoij 8/29/2016 #1

问题是你不能遍历一个字符串(例如,在Python中)。您首先需要将其转换为数组:.split

for c in string.split
    string2 = c + string2
end

话虽如此,你不应该在 Ruby 中使用循环。它们在内部被转换为一种方法,因此您犯了令人困惑的错误。最好从一开始就写:foreacheach

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

任何 Ruby 程序员都不会在任何情况下使用,它只被刚接触 Ruby 的人使用;-)for


请注意,Array.each 只是一个迭代方法;例如,还有 String.each_char 方法:

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

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

评论

0赞 Brandon Hill 9/2/2016
非常感谢您的意见!我是 Ruby 和编码的新手,所以我有很多东西要学。
0赞 dercz 8/29/2016 #2

你要找的是:

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

评论

0赞 Brandon Hill 8/29/2016
谢谢你!
1赞 dercz 8/29/2016
@BrandonHill实际上 Carpetsmoker 的答案要完整得多,如果你对检查长字符串感兴趣,你可能想只遍历字符串一次,检查第一个字母和最后一个字母,检查第二个字母和倒数第二个字母,等等。 还要注意,在 Ruby 中,除非你想破坏控制流,否则你不必使用 return, 因为它是应用语言,并且函数中最后一个表达式的值无论如何都是它的返回值。祝你学习Ruby好运!
0赞 TeWu 8/29/2016 #3

你可以这样写:

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

评论

1赞 Blue 8/29/2016
虽然这个代码片段可能会解决这个问题,但包括一个解释确实有助于提高你的帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要在代码中塞满解释性注释,因为这会降低代码和解释的可读性!
2赞 pjs 8/29/2016 #4

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

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

如果你想更加面向对象,你可以把它转换为类 String 中的方法:

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

p "abcba".palindrome?  # => true

注意 — 编辑以利用 Cary Swoveland 关于使用而不是从块中显式返回的出色建议。这使它成为单行。all?

3赞 Todd A. Jacobs 8/29/2016 #5

TL;博士

除了效率低下之外,您的代码不起作用,因为 String 不是 Array,也不会混合在 Enumerator 中以提供 #each 方法。

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

了解异常

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

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

NoMethodError:“abcba”:字符串的未定义方法“each”

这是相当荒谬的。它告诉你 String 没有 #each 方法,这就是语法糖在引擎盖下真正调用的内容。如果要迭代,则需要某种形式的 EnumeratorEnumerable 来使用。for i in string

遍历字符串

String 类具有许多有用的方法,用于将字符串转换为可迭代对象。一些例子包括:

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

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

然而,虽然这突出了如何解决异常,但它仍然是不必要的复杂和低效的。

利用内置方法

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

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

palindrome? 'abcba'
#=> true

palindrome? 'abcde'
#=> false

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

评论

0赞 Brandon Hill 9/2/2016
感谢您抽出宝贵时间向我解释这一点。很有帮助!
0赞 Todd A. Jacobs 9/2/2016
@BrandonHill 欢迎使用 Stack Overflow。不需要感谢。在 Stack Exchange 上表达您感激之情的正确方式是为您认为有用的答案投赞成票(如果您愿意,您可以投多个答案的赞成票),并打勾以接受对您最有帮助的答案。