为什么我的 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#each_char 可以传递一个块,或者 return 一个枚举器
- String#chars return 一个数组
- String#split 也是 return 一个数组(例如
'abcba'.split //
)
例如,您的代码可以重构为使用如下块:
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 核心的来龙去脉时,这种区别可能是一个真正的帮助,但在这种情况下,无论哪种方式,结果都是相同的。
我在让 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#each_char 可以传递一个块,或者 return 一个枚举器
- String#chars return 一个数组
- String#split 也是 return 一个数组(例如
'abcba'.split //
)
例如,您的代码可以重构为使用如下块:
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 核心的来龙去脉时,这种区别可能是一个真正的帮助,但在这种情况下,无论哪种方式,结果都是相同的。