【问题标题】:Difference between 3-dot-range operator and 2-dot-range operator in flip flop ruby触发器红宝石中3点范围运算符和2点范围运算符之间的区别
【发布时间】:2014-07-17 06:58:33
【问题描述】:

请帮助我理解范围运算符 ..... 作为 Ruby 中使用的“触发器”之间的区别。

这是来自 Ruby 实用程序员指南的一个示例:

a = (11..20).collect {|i| (i%4 == 0)..(i%3 == 0) ? i : nil}

返回:

[nil, 12, nil, nil, nil, 16, 17, 18, nil, 20]

还有:

a = (11..20).collect {|i| (i%4 == 0)...(i%3 == 0) ? i : nil}

返回:

[nil, 12, 13, 14, 15, 16, 17, 18, nil, 20]

【问题讨论】:

标签: ruby flip-flop


【解决方案1】:

触发器(又名 f/f)是一个源自 perl 的有状态运算符。

f/f 运算符隐含在 ruby​​ 的条件语句(if 和三元)中,而不是 range 所以 (1..5) 是一个范围但是 (1..5) ? 1 : 5 是 f/f。 f/f 有一个内部状态(真/假),由两个条件组成。 当它的第一个条件评估为真时它会打开(状态变为真),当它的第二个条件评估为真时它会关闭。 两个和三个虚线版本的区别在于 双点在第一个条件评估为真之后立即评估第二个条件,并且 三点没有。

两个虚线版本是这样的:

A..B |
A -> false | State -> false
A -> true, B -> false | State -> true # notice how it checks both conditions
B -> false | State -> true
B -> true | State -> false
A -> false  | State -> false
A -> true, B -> true | State -> false

比较一下三个点的版本

A...B
A -> false | State -> false
A -> true | State -> true # three dotted version doesn't check second condition immediately
B -> false | State -> true
B -> true | State -> false
A -> false | State -> false
A -> true | State -> true

让我们继续 amazing perl article,但使用 ruby​​ 中的示例

两个虚线示例:

DATA.each_line do |line|
  print "\t" if (line =~ /^start/ .. line =~ /^end/)
  print line
end

__END__
First line.
start
Indented line
end
Back to left margin

打印出来:

First line.
    start
    Indented line
    end
Back to left margin

如您所见 - f/f 在第 2 行打开,在第 4 行关闭。 然而,它有一个微妙之处。看看这个:

DATA.each_line do |line|
  print "\t" if (line =~ /start/ .. line =~ /end/)
  print line
end
__END__
First line.
Indent lines between the start and the end markers
Back to left margin

打印出来:

First line.
    Indent lines between the start and the end markers
Back to left margin

它在第 2 行打开并立即关闭。

假设您不想在第一个运算符之后立即检查第二个运算符。 这就是三点 f/f 派上用场的地方。查看下一个示例。

DATA.each_line do |line|
  print "\t" if (line =~ /start/ ... line =~ /end/)
  print line
end

__END__
First line.
Indent lines between the start and the end markers
So this is indented,
and this is the end of the indented block.
Back to left margin

哪些打印:

First line.
    Indent lines between the start and the end markers
    So this is indented,
    and this is the end of the indented block.
Back to left margin

如您所见,它在第 2 行打开,在第 4 行关闭

现在让我们将它应用到您的示例中

我写了一个小脚本来说明它的行为

def mod(n, i)
  result = i % n == 0
  puts "#{i} mod #{n} => #{result}"
  result
end

(11..20).each { |i|
  if (mod(4, i))...(mod(3, i)) # NOTE it's a three dotted version
    # NOTE that those puts show previous state, not the current one!
    puts true
  else
    puts false
  end
}

两个虚线结果:

11 mod 4 => false
false
12 mod 4 => true
12 mod 3 => true # Notice how it checks both conditions here
true
13 mod 4 => false
false
14 mod 4 => false
false
15 mod 4 => false
false
16 mod 4 => true
16 mod 3 => false
true
17 mod 3 => false
true
18 mod 3 => true
true
19 mod 4 => false
false
20 mod 4 => true
20 mod 3 => false
true

三个点的结果:

11 mod 4 => false
false
12 mod 4 => true
true
13 mod 3 => false
true
14 mod 3 => false
true
15 mod 3 => true # turns OFF here
true
16 mod 4 => true # and turns immediately ON here
true
17 mod 3 => false
true
18 mod 3 => true
true
19 mod 4 => false
false
20 mod 4 => true
true
=> 11..20

附:范围和触发器是两个完全不同的运算符,您不应该混淆它们。

【讨论】:

    【解决方案2】:

    触发器开关的概念其实来自electronics。它的主要优点是它记住它的状态。将触发器布尔范围视为变量,存储布尔值。我们来看看下面的例子:

    1.upto(10).each do |i|
      puts i if (i%2==0)..(i%4==0)
    end
    
            #                        vvvv   value of “hidden” state
    2       # left range boundary  ⇨ true
    3
    4       # right range boundary ⇨ false, no 5 follows 
    6       # left range boundary  ⇨ true
    7
    8       # right range boundary ⇨ false, no 9 follows 
    10
    

    在您的第一个示例中,“条件”对于 4 倍数变为 true,在 3 倍数时又变为 false。在第二个示例中,12 上的条件变量没有被关闭,只是因为右范围边界 (i%3) 被排除,因为它是一个 3 点范围。

    希望这个例子不要太纠结。

    【讨论】:

    • 是的。它在那里,但直到现在我才看到在任何代码中使用触发器。
    【解决方案3】:

    Ruby 中 2 点和 3 点的区别在于包含。例如

    (1..100)
    => All numbers starting from 1 and ending at 100 INCLUDING 100
    
    (1...100)
    => All numbers starting from 1 that are less than 100
    
    (1..100).include?(100)
    => true
    
    (1...100).include?(100)
    => false
    

    希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      当在一个范围内使用双点时,它会创建一个数字范围,该范围会上升并包含传入的最大数字。当使用三点时,它会创建一个上升到但不包括最大值的范围传入的号码。

      所以:

       (1..5).each {| i | puts i} #will print 1,2,3,4,5
      

      同时:

      (1...5).each {| i | puts i} #will print 1,2,3,4
      

      【讨论】:

      • 但是使用点运算符作为触发器,它会有不同的行为。操作数不可迭代 (i%4 == 0)..(i%3 == 0).
      猜你喜欢
      • 2011-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-08
      • 1970-01-01
      • 1970-01-01
      • 2011-01-21
      • 1970-01-01
      相关资源
      最近更新 更多