5

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

4 回答 4

4

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

f/f 运算符隐含在 ruby​​ 的条件语句(if 和三元)中而不是 range 所以 (1..5) 是一个范围但是 (1..5) ?1 : 5 是 af/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

让我们继续阅读令人惊叹的 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

PS Range 和 Flip/flop 是两个完全不同的运算符,您不应混淆它们。

于 2018-08-03T20:04:42.353 回答
3

触发器开关的概念实际上来自电子学。它的主要优点是它记住了它的状态。将触发器布尔范围视为变量,存储布尔值。让我们看一下以下示例:

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

在您的第一个示例中,“条件”变成true了 4 倍数,然后又变成了false3 倍数。12在第二个示例中,条件变量没有因为右范围边界 ( i%3) 被排除而关闭,因为它是一个 3 点范围。

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

于 2014-07-17T08:44:15.417 回答
0

当在一个范围内使用双点时,它会创建一个数字范围,该范围包括传入的最大数字。当使用三点时,它会创建一个范围,但不包括传入的最大数字.

所以:

 (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
于 2014-07-17T07:40:45.277 回答
0

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

希望这可以帮助。

于 2014-07-17T15:19:46.787 回答