#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 
 

Я не уверен, возвращает ли это ожидаемый результат, но вы поняли идею.