Приоритет в блоке одинарного `&` - PullRequest
0 голосов
/ 13 июня 2018

Рассмотрим следующий код Ruby:

[1,3].any? &:even? || true
# => false
[1,3].any? &nil || :even?
# => false
[1,3].any? &nil || :odd?
# => true

Таким образом, похоже, что Boolean-или || имеет более высокий приоритет, чем унарный код &.Я этого не ожидал.Это верно и задокументировано ли это где-нибудь?

Ответы [ 2 ]

0 голосов
/ 13 июня 2018

Когда & не предшествует какой-либо объект (унарный), и сразу за ним следует что-либо еще (&nil в данном случае).Он будет проанализирован как попытка вызова метода to_proc, в противном случае он будет рассматриваться как вызов метода для получателя, например

 nil&nil
 #=> false  

Эквивалентно nil.&(nil)

Так что в вашем случае([1,3].any? &nil || :even?) он анализируется как [1,3].any?(&(nil || :even?)), поскольку & (вызов метода to_proc) имеет более низкий приоритет, чем логический ||, и для продолжения необходимо знать результат nil || :even?.

Однако для работы и (&) требуется приемник (не унарный), но он имеет более высокий приоритет, чем логический или (||), например [1,3].any? &nil&nil || :even?, который оценивается как

[1,3].any?(&(nil.&(nil) || :even?)
#=> false
# Or
[1,3].any?(&(nil & nil || :even?)
#=> false

Где странно, ясно, что операционный и (&) имеет более высокий приоритет, чем логический или (||)

Дополнительные примеры

true & nil || :even? 
#=> :even?

true & :even? 
#=> true

[1,3].any? &true&true || :even?
#=> TypeError: wrong argument type TrueClass (expected Proc)

# Okay no problem

class TrueClass
  def to_proc
    ->(*_) { true } 
  end
end 

[1,3].any? &true&true || :even?
#=> true
[1,3].any? &true || :even?
#=> true

Даже незнакомецчто унарный & допускает nil, но по существу игнорирует его из block_given?, но любой другой объект, который не реализует to_proc, поднимает TypeError

def no_block
  block_given? ? yield('Yes') : 'No'
end

no_block &nil
#=> "No"

no_block &false
#=> TypeError: wrong argument type FalseClass (expected Proc)
0 голосов
/ 13 июня 2018

Для этого и используются ключевые слова and и or.Вы должны написать это как

[1,3].any? &:even? or true

Что касается того, почему это происходит - я не могу найти документацию для этого - но я думаю, что это на самом деле больше связано с необязательными скобками и ограничениямиодинарный &.

Унарный & особенный.«Нормальные» операторы, такие как ~, по сути, являются синтаксическим сахаром над вызовами методов;Вы можете поместить их где угодно.Но & допускается только только в аргументах метода , и даже тогда только в конце.

foo x, &bar
# NameError, determined at runtime because it has to see if any of these names are defined
foo &bar, x
# SyntaxError! Didn't even make it past the parser

y = bar
# NameError
y = &bar
# SyntaxError!

И когда вы оставляете скобки из вызова метода, оно в значительной степени выпадаетвсе, останавливаясь только на вещах со сверхнизким приоритетом, например, если / если / и / или / или.

foo bar baz if true
# same as
foo(bar(baz)) if true

Так что ваш пример эквивалентен

[1,3].any?(&:even? || true)

Теперь, если & быликаким-то образом с высоким приоритетом это либо полностью нормальное значение для оценки во время выполнения true, либо это строго ограниченная специальная синтаксическая конструкция &:even?.Не очень приятно обнаруживать синтаксические ошибки во время выполнения, поэтому, я думаю, разработчики решили решить эту проблему простым способом: сделать & супер низким приоритетом.Таким образом, синтаксический анализатор может просто проверять правила синтаксиса и игнорировать сам аргумент блока (который должен оцениваться во время выполнения).

...