触发器ruby中3点范围运算符与2点范围运算符的区别



请帮助我理解范围运算符.....在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]

flip/flop(又名f/f)是一种源自perl的有状态操作符。

f/f操作符隐含在ruby的条件语句(if和三元)中,而不是range所以(1..5)是一个范围,但是(1..5)?1:5是f/f。F/F有一个内部状态(true/false),由两个条件组成。当第一个条件为真时,它打开(状态变为真),当第二个条件为真时,它关闭。两个点和三个点的区别在于在第一个条件求值为true和之后,two- point立即求第二个条件three-dotted没有。

两个点分隔的版本如下:

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

让我们继续阅读这篇精彩的perl文章,但要使用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

注:Range和flip/flop是两个完全不同的操作符,你不应该混淆它们。

触发器的概念实际上来自电子学。它的主要优点是它记住它的状态。考虑触发器布尔范围作为一个变量,存储布尔值。让我们看一下下面的例子:

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点范围。

希望这个例子没有太纠结。

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

当在范围中使用双点时,它创建了一个数字范围,该范围包括传入的最大数字。当使用三重点时,它将创建一个范围,该范围将增加到但不包括传入的最大数量。

:

 (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

最新更新