Ruby:使用此表达式忽略循环中的第一个输入,帮助理解此表达式

时间:2016-10-26 23:05:53

标签: ruby loops iteration

我需要一个循环来逐行读取文本但完全忽略第一行。我遇到了Nobuyoshi Nakada在线发布的非常方便的表达方式。此表达式跳过Enumerator的一次迭代。有人可以帮助解释评估的最后一步吗?这是举例说明。这段代码将打印1到5而不是0到5.我是编程新手,我不知道这是否是一个常见的解决方案。我花了3天才找到它!

(0..5).each do |i|
  if (first=true)..false and first
    next
  end
  puts i
end

我推荐使用if (first=true)..false and first评估pry,但无法深入了解。

  1. (first=true)评估为: true first的值为 true

  2. (first=true)..false评估为: true .. false(first的值仍为 true

  3. (first=true)..false and first评估为: true .. false和 true true true first的值仍为 true

  4. if (first=true)..false and first评估为:if( true ),执行 next 语句并退出 if 循环(值为first现在 nil

  5. Enumerator的下一次迭代返回if (first=true)..false and first表达式(第一个值仍 nil

  6. 此步骤评估为 false if(first = true)..false and nil(左侧first的值为 true ,右侧为的(适用的) 我迷失在这里......

  7. 问题:为什么if {@}的右侧first未分配" true "。 first的值似乎同时为真?我在这里错过了什么吗?

3 个答案:

答案 0 :(得分:2)

几个关键见解:

    left..right中的{li> if是一个触发器操作符,如JörgWMittag所述:它只评估左侧(和左侧)它是假的(返回该值),然后评估右侧(和右侧),只要它是真实的(返回true),然后返回评估左侧。这允许您编写if (row == 5)..(row == 10)之类的内容并获取从5到10的行(因为它从左侧返回false从1到4,然后从true从左侧返回5然后翻转并评估右侧从6到10,每次返回true,最后一次翻转到10;然后返回到返回false左边的其余行侧)。
  • 因为在块之外没有提到first,所以它是一个作用于块的变量;这意味着它的值不会在步骤之间延续,并在每次迭代中重置为nil

所以:

  • 在第一步中,(first = true)true;触发器返回true并翻转到右侧
  • true and first评估为true;处理next,循环循环。
  • 在第二步中,局部变量first返回nil
  • 触发器正在评估右侧 - false - 它没有翻转,并返回true
  • true and firstfalse; next 评估
  • 第三步和后续步骤都与第二步相同,因为没有任何变化:触发器将评估右侧,first将继续为nil
编辑:一个重要的错字:P

答案 1 :(得分:2)

The flip-flop operator是从Perl继承的Ruby的一个有点模糊的特性(就像大多数Ruby的更加模糊的特性一样)。它是一种在truefalse之间来回翻转和翻转的表达式。

基本上,它从false开始,然后它翻转"翻转"只要表达式评估为 truthy 对象,就true,并且"翻转"只要表达式计算为 truthy 对象,就会再次返回false

因此,条件以false开头。 第一次通过循环, left 表达式计算为 truthy 值(在Ruby中,赋值计算为正在分配的值)并且作为副作用还会将true分配给first。由于 left 表达式的计算结果为 truthy ,因此触发器翻转为true,整个条件现为true and true,即true {1}}。因此,我们执行条件表达式的主体,它只是next,即跳过当前的迭代。

(以及所有其他时间)通过循环,触发器已经翻转到true,所以现在我们只有检查表达式,以便触发器再次翻转回false。不再检查表达式。好吧,正确的表达只是false所以我们永远不会再次回到false,触发器现在总是true。但是,左表达式不会被评估(我们只检查右表达式何时再次翻转),这意味着永远不会执行first的赋值,这意味着first是初始化的局部变量,这意味着它的计算结果为nil,这是 falsy ,这意味着true and nil的计算结果为false。对于循环的每次迭代,这将继续如此。

因此,这段代码以更加模糊的方式使用了一个模糊的特征(触发器)(不是作为条件,而是简单地用于#34;禁用"执行赋值)。这绝对是任何Rubyist都会编写的 not 代码。

是一种更惯用的方式
(0..5).drop(1).each do |i|
  puts i
end

(0..5).each.with_index do |n, idx|
  next if idx.zero?
  puts n
end

请注意,nobu是Ruby核心开发人员,日语,也是最早使用Ruby的人之一。日本的Ruby早期采用者是一个与更大的Ruby社区完全不同的子社区。很可能这实际上是日本Rubyists和/或Ruby核心开发人员中众所周知且易于理解的习惯用语。关于习语的事情是,如果你知道它们的样子并且你知道它们的意思,你就不需要理解它们,所以像这样的代码可能可以,如果这是惯用的。但它确实在较大的Ruby社区中惯用。

答案 2 :(得分:-1)

(first=true)..falsetrue..false的评价是正确的,但这不是有效的陈述 - 它会引发ArgumentError: bad value for range

也许其他人了解这个解决方案是如何运作的,但我认为这绝对不是最简单或最简单的方法。

如果您的目标是获得N次输入并忽略N个输入,则可以执行以下操作:

num_times_to_ignore = 1
num_times_to_process = 3

# ignore some lines
num_times_to_ignore.times { gets.chomp }

# process each input individually
num_times_to_process.times.each { do_something_with(gets.chomp) }

# or you can get all the inputs before processing
results = num_times_to_process.times.map { gets.chomp }
results.each { |input| do_something_with(input) }