#ruby
#рубиновый
Вопрос:
Приведенный ниже класс противоречит моему пониманию, который nil amp;amp; 'foo'
должен возвращать nil и не выполнять ‘foo’
независимо от того, что я пробовал, со скобками или без них, @user.another_boolean
всегда возвращает неопределенный метод another_boolean для nil nilclass . Я подумал, что если @user равен нулю, он должен прекратить оценку там и вернуть nil.
class MyClass
def initialize(user, variable = nil)
@user = user
@variable = variable || user.try(:variable)
end
def result
@result ||= !!(@user amp;amp;
@variable amp;amp;
@variable.a_boolean ||
@user.another_boolean? ||
@user.a_third_boolean? amp;amp; instance_method_retuning_a_boolean)
end
end
Я также попытался найти документацию оператора amp;amp; в документации ruby, но смог найти только ссылку and
, которая не должна совпадать, учитывая разницу в их приоритете.
Любая помощь очень ценится.
Версия Ruby: 2.2.5
Редактировать: @user и @variable — это модель rails
Версия Rails: 4.2
Комментарии:
1. Ваш вопрос сбивает с толку: another_boolean …. Вы никогда не вызываете метод с таким именем. Единственное , что я вижу , это то , что вы вызываете метод
another_boolean?
. Пожалуйста, всегда публикуйте точное сообщение об ошибке.
Ответ №1:
Это стандартная практика в программном обеспечении amp;amp;
для иметь более высокий приоритет, чем ||
.
Итак, все следующие логически эквивалентны:
b amp;amp; a || c
a amp;amp; b || c
c || b amp;amp; a
c || a amp;amp; b
Теперь ваш код немного длиннее:
@user amp;amp;
@variable amp;amp;
@variable.a_boolean ||
@user.another_boolean? ||
@user.a_third_boolean? amp;amp; instance_method_retuning_a_boolean
Но опять же, мы можем сгруппировать amp;amp;
операторы вместе, чтобы показать, чему это эквивалентно:
(@user amp;amp; @variable amp;amp; @variable.a_boolean) ||
(@user.another_boolean?) ||
(@user.a_third_boolean? amp;amp; instance_method_retuning_a_boolean)
Поэтому, если @user amp;amp; @variable amp;amp; @variable.a_boolean == false
, то @user.another_boolean?
будет оценено.
Мне не совсем понятно, чего вы пытаетесь достичь, поэтому я не знаю, верна ли приведенная выше логика или как ее можно «исправить», но есть ваше объяснение, почему вызывается метод.
Комментарии:
1. Спасибо, что это было полезное объяснение.
Ответ №2:
Ваше выражение имеет вид:
aaa amp;amp;
bbb amp;amp;
bbb.foo ||
aaa.bar ||
aaa.baz amp;amp; something
он может быть переформатирован как:
aaa amp;amp; bbb amp;amp; bbb.foo
||
aaa.bar
||
aaa.baz amp;amp; something
Это то же самое, просто пробелы расположены по-другому.
Как вы можете видеть здесь, не все термины защищены начальным тестом aaa amp;amp;bbb.
Скорее всего, вы имели в виду это:
@result ||= !!( (
@user amp;amp;
@variable
)
amp;amp;
(
@variable.a_boolean ||
@user.another_boolean? ||
@user.a_third_boolean?
)
amp;amp; instance_method_retuning_a_boolean
)
Я добавил слишком много круглых скобок, чем нужно, но таким образом вы точно видите, что происходит.
Ответ №3:
Привет, Ян, и добро пожаловать в Stackoverflow. Позвольте мне привести вам несколько примеров, которые могут помочь вам понять причину вашего наблюдения.
Вы правильно заявили, что:
nil amp;amp; true
=> nil
но если вы объединяете дополнительные операторы без явного использования скобок, то произойдет следующее:
nil amp;amp; true || true
=> true
Это связано с тем, что amp;amp;
оператор имеет более высокий приоритет, поэтому вы можете написать то же самое, что и это, и тогда становится ясно, почему выражение не останавливается после первого нуля:
(nil amp;amp; true) || true
Я нашел эту статью довольно полезной: https://womanonrails.com/operator-precedence-ruby .
Итак, для вашего случая, если бы мы поставили скобки так, как сейчас, у нас было бы следующее:
(@user amp;amp; @variable amp;amp; @variable.a_boolean) ||
@user.another_boolean? ||
(@user.a_third_boolean? amp;amp; instance_method_retuning_a_boolean)
Это означает, что даже если первая часть выражения приводит к false , @user.another_boolean?
все равно вычисляется.
Итак, что вы, вероятно, хотите, это явно ставить скобки:
(@user amp;amp; @variable) amp;amp;
(@variable.a_boolean || @user.another_boolean? || @user.a_third_boolean?) amp;amp;
instance_method_retuning_a_boolean
Итак, теперь у вас есть первая часть, которая проверит, не равны ли @user и @variable нулю. Если какой-либо из них равен нулю, вторая часть больше не будет оцениваться.
Надеюсь, это внесет некоторую ясность.
Ответ №4:
Вероятно, вы можете избежать чрезмерно сложного логического выражения, добавив защитное предложение (или два), которое отделяет предварительные условия от фактического результата:
def result
return unless @user
return unless @variable
@result ||= @variable.a_boolean ||
@user.another_boolean? ||
@user.a_third_boolean? amp;amp; instance_method_retuning_a_boolean
end
Я не уверен, возвращает ли это ожидаемый результат, но вы поняли идею.