Разница между оператором диапазона с тремя точками и оператором диапазона с двумя точками в flip flop ruby

Пожалуйста, помогите мне понять разницу между операторами диапазона ... и .. как «триггерами», используемыми в Ruby.

Это пример из руководства Pragmatic Programmers по 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]

person gmuraleekrishna    schedule 17.07.2014    source источник
comment
возможный дубликат Разница между '..' (двойной- точка) и '...' (тройная точка) в генерации диапазона?   -  person Зелёный    schedule 17.07.2014


Ответы (4)


Разница между двумя и тремя точками в Ruby - это включение. Например

(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

Надеюсь это поможет.

person George Lucas    schedule 17.07.2014

Триггер / флоп (он же f / f) - это оператор с отслеживанием состояния, происходящий из Perl.

Оператор f / f подразумевается в условных операторах (if и ternary) в ruby ​​вместо диапазона, поэтому (1..5) - это диапазон, но (1..5)? 1: 5 - это п / ф. f / f имеет внутреннее состояние (истина / ложь) и состоит из двух условий. Он настраивается на ON (состояние становится истинным), когда его первое условие оценивается как истинное, и OFF, когда его второе условие оценивается как истинное. Разница между версиями с двумя и тремя точками заключается в том, что с двумя точками оценивается второе условие сразу после того, как первое оценивается как истинное, а с тремя точками - нет.

версия с двумя точками работает так:

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

P.S. Диапазон и триггер - это два совершенно разных оператора, и вы не должны путать их.

person stefkin    schedule 03.08.2018

Идея триггерного переключателя фактически пришла из 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

В вашем первом примере «условие» превратилось в true для 4-кратных и снова превратилось в false для 3-кратных. Во втором примере переменная условия не была отключена на 12 только потому, что правая граница диапазона (i%3) исключена, поскольку это диапазон из трех точек.

Надеюсь, пример не был слишком запутанным.

person Aleksei Matiushkin    schedule 17.07.2014
comment
да. Он есть, но до сих пор я никогда не видел использования триггера в каких-либо кодах. - person Arup Rakshit; 17.07.2014

Когда в диапазоне используется двойная точка, она создает диапазон чисел, который увеличивается до максимального переданного числа и включает его. Когда используется тройная точка, он создает диапазон, который достигает, но НЕ включает максимальное число, переданное в .

So:

 (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
person James    schedule 17.07.2014
comment
Но если использовать точечные операторы в качестве триггеров, он будет вести себя иначе. операнды не повторяются (i% 4 == 0) .. (i% 3 == 0). - person gmuraleekrishna; 17.07.2014