【问题标题】:String incrementer codewars (Ruby) problemString incrementer codewars (Ruby) 问题
【发布时间】:2020-06-07 15:40:06
【问题描述】:

我正在开发一个关于 codewars 的字符串增量器项目。 基本上, 编写一个递增字符串的函数,以创建一个新字符串。

如果字符串已经以数字结尾,则该数字应加 1。 如果字符串不以数字结尾。数字 1 应附加到新字符串中。 如果数字有前导零,则应考虑位数。 foo -> foo1

foo001 ->foo002

foobar23 -> foobar24

foo099 -> foo100

我的代码是:input.gsub(/\d/,"")+input.split().map {|x| x[/\d+/].next!}.join(" ") https://repl.it/@tanilserbes/ViolentNoteworthyDowngrade 。它适用于这个游乐场 但是它不适用于代码战。我收到此错误:

main.rb:5:in block in increment_string': undefined methodnext!'对于 nil:NilClass (NoMethodError)` 有什么想法吗?
提前致谢!

【问题讨论】:

  • 我得到相同的 NoMethodError 和 input"foo"
  • 另外,您可能想了解String#succ 的作用。
  • "如果数字有前导零,则应考虑位数" 那么"foo99" 如果没有前导 0,会发生什么?
  • 我觉得String#succ 可能对这个没有好处,因为它会将"foo" 增加到"fop""foo9""fop0"...只是增加你需要做的检查次数,imo...尽管string.sub!(/(\d+)\Z/, &:next) || string << "1" 应该足够好
  • 你得到了“undefined methodnext!”对于零:NilClass”。 99.9% 的情况下,这意味着您的变量或类名拼写错误。请参阅“How to Ask”、“Stack Overflow question checklist”和“MCVE”及其所有链接页面。

标签: arrays ruby string increment codewarrior


【解决方案1】:

在开发解决方案时需要考虑以下几点。

如果你的字符串是:

str = "ca9t00456"

期望的返回值是:

"ca9t00457"

(请注意,OP 的解决方案会为此字符串返回不正确的结果 ("cat10")。Codewars 问题并没有说字符串中唯一的数字是末尾的数字;它只提到了“数字”字符串的结尾”。)

合理的第一步是将字符串分成两部分:

n = str.index(/\d+\z/)
  #=> 4
prefix = str[0, n]
  #=> "ca9t"
suffix = str[n..-1]
  #=> "00456"

String#index。正则表达式 /\d+\z/ 读取为“匹配一个或多个 (+) 数字 (\d),后跟字符串结尾 (\z)。数字 '9' 被跳过,因为它是既不跟数字也不在字符串的末尾。另请参阅String#[]

我们返回的字符串将以prefix 开头(保存的值),所以我们可以暂时把它放在一边,专注于修改suffix

一种方法是:

((suffix.to_i) + 1).to_s
  #=> "457"

但是我们必须添加正确数量的前导零。这里这与suffix (2) 中前导零的数量相同,但如果suffix 是,例如00999,它将只有一个(01000)。可以这样做,但是很乱。

更简单的方法是使用方法String#succ,正如@steenslag 在 cmets 中建议的那样。

new_suffix = suffix.succ 
  #=> "00457"
"00999".succ
  #=> "01000"

现在我们只需要合并prefixnew_suffix

注意如果我们对整个字符串执行succ会发生什么:

"ca9t0456".succ
   #=> "ca9t0457" correct
"ca9t0999".succ
   #=> "ca9t1000" correct
"ca9t9999".succ
   #=> "ca9u0000" incorrect

如您所见,第三个示例存在问题。这就是为什么我第一步选择将字符串分成两部分。

您需要调查另外三个案例。第一种是当前缀为空字符串时:

str = "00456"

第二种是后缀为空字符串时:

str = "ca9t"

第三个是字符串为空的时候:

str = ""

您可以检查之前的计算在第一种情况下是否仍然有效。

在第二种情况下,我们会发现:

n = str.index(/\d+\z/)
  #=> "cat9t".index(/\d+\z/) => nil

nnil 值告诉我们所需的返回值是:

str + "1"
  #=> "ca9t" + "1" => "ca9t1"

这行得通吗?

【讨论】:

  • 谢谢卡里。我什至没有开发我的解决方案,我解决了它!感谢您一步一步的展示。
【解决方案2】:

要查看这里发生了什么,单独运行每个命令并查看输出以跟踪错误是有益的:

input = "foo"
input.gsub(/\d/, "") # => "foo"

所以+ 运算符的左侧将变为“foo”,现在我们需要看看右侧是什么:

input.split # => ["foo"]
["foo"].map { |x| x[/\d+/].next! }

从问题中可以看出,这是发生错误的地方,所以让我们深入研究map 块内的代码,错误所在:

["foo"].map { |x| p x }
# Outputs: "foo"

所以,x == "foo" 在这一点上:

["foo"].map { |x| p x[/\d+/] }
# Outputs: nil

由于字符串"foo" 中没有任何数字,正则表达式从中提取数字,以增加它们返回nil,然后在没有任何保护的情况下增加它。 NilClass 没有方法 next!,所以你得到了你的错误。

如果字符串改为"foo1",你会得到:

["foo1"].map { |x| p x[/\d+/] }
# Outputs: "1"

它返回匹配的字符串,然后允许您在其上调用next!(这是在 cmets 中调用的String#succ! 方法的同义词)。它在操场上工作的原因是因为字符串中有数字,并且没有考虑或测试字符串没有的情况(问题文本中的第一个示例“foo”应该变为“foo1 ")。

【讨论】:

  • 谢谢!你和Carry(下面的评论)家伙从头到尾解释了一切。非常感激!我怎么会错过这个错误?!再次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-10-14
  • 1970-01-01
  • 2023-01-17
  • 1970-01-01
  • 2021-03-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多