Отрицательный условный оператор в Ruby

#ruby #syntax #conditional

#ruby #синтаксис #условные операторы

Вопрос:

Похоже, что среда разработки RubyMine выдает предупреждение всякий раз, когда видит отрицательный условный оператор. Мне интересно, почему использование отрицательного условного оператора плохо? Это просто из-за удобства чтения?

Например, в этом коде:

 class Complement
    def self.of_dna dna_strand
      dna_array =  dna_strand.chars
      dna_complement  = ['']
      dna_structure = ['C', 'G', 'T', 'A']

      dna_array.each do |strand|
        unless dna_structure.include? strand 
          return ''
        end

        case strand
        when "C"
         dna_complement << "G"
        when "G"
         dna_complement << "C"
        when "T"
         dna_complement << "A"
        when "A"
         dna_complement << "U"
        end
      end
    dna_complement.join('')
  end
end
  

Мне интересно, в чем разница между unless dna_structure.include? strand и if !(dna_strucutre.include?) в этом случае?

Комментарии:

1. Вы могли бы написать свой case оператор dna_complement << case strand; when "C" then "G"; when "G" then "C"; when "T" then "A"; when "A" then "U"; end . В качестве альтернативы вы могли бы написать dna_complement << strand.tr("CGTA", "GCAU") .

Ответ №1:

Поскольку в Ruby есть не только if , но unless , рекомендуется использовать его до тех пор, пока результирующий код понятен. То есть вы должны преобразовать что-то вроде этого:

 if (!string.empty?)
  # ...
end
  

Во что-то вроде этого:

 unless (string.empty?)
  # ...
end
  

Для этого есть исключения, например, когда у вас есть это:

 if (!string.empty?)
  # ... when not empty
else
  # ... when empty (when not not empty)
end
  

Наивным подходом было бы преобразовать это в unless , но это создает тройное отрицание. Здесь вы уже имеете дело с double, else предложение выполняется только в том случае, если строка не пустая или, возможно, ничего не не не не содержит, не не не не не содержит, не не не не не содержит. не не не не не содержит. не не не не не содержит.

Сделайте это вместо:

 if (string.empty?)
  # ... when empty
else
  # ... when not empty
end
  

Существует ряд проблем с подходом, который вы используете здесь, но самая серьезная из них заключается в том, что вы объявляете постоянный массив внутри вашего метода каждый раз, когда метод вызывается. Поскольку это никогда не меняется, сделайте это константой на верхнем уровне класса. По крайней мере:

 class Complement
  DNA_STRUCTURE = %w[ C G A T ]
end
  

Еще лучше было бы использовать таблицу сопоставления для представления пар:

 COMPLEMENT = {
  'C' => 'G',
  'G' => 'C',
  'T' => 'A',
  'A' => 'U'
}.freeze
  

Теперь, глядя на вашу конкретную проблему, когда вы пытаетесь «инвертировать» заданную последовательность символов, инструмент, который вам действительно нужен, находится tr в самой строке, метод, который оптимизирован для обработки таких вещей, как шифры, где между символами есть сопоставление 1: 1.

Вся ваша функция сворачивается до этого:

 def self.of_dna(strand)
  strand.tr('CGTA', 'GCAU')
end
  

Теперь, если вы хотите выполнить быстрый тест, чтобы убедиться, что вы действительно имеете дело с допустимой последовательностью:

 def self.of_dna(strand)
  return '' unless (strand.match(/A[CGTA]*z/))

  strand.tr('CGTA', 'GCAU')
end
  

Здесь вы сталкиваетесь с некоторыми другими вредными привычками, такими как создание массивов для хранения отдельных символов, когда строки намного лучше справляются с этой конкретной задачей. c = '' и тогда c << 'G' было бы эффективнее, чем массивная версия same, особенно учитывая, что массив будет содержать N строк, каждая из которых несет некоторые накладные расходы и требует создания другой строки в конце с помощью join . При использовании Ruby старайтесь свести количество объектов, необходимых для выполнения ваших вычислений, временных или иных, к минимуму. Обычно это быстрее с меньшим количеством «мусора».

Комментарии:

1. ваш ответ вдвойне хорош за то, что вы показали альтернативу оператору case!

2. @tadman Вау, большое тебе спасибо. Вы не только ответили на мой вопрос, но и дали много ценных советов. Сейчас я проведу рефакторинг своего кода.

3. @tadman Не могли бы вы также, пожалуйста, объяснить мне регулярное выражение? Я не понимаю, что A и z означает в начале и в конце регулярного выражения.

4. Если вы не знакомы с регулярными выражениями, загляните на такой сайт, как Rubular , чтобы получить пояснения и простой способ запуска тестов.

Ответ №2:

В последней форме нет ничего плохого, но, учитывая, что в ruby у нас есть unless , мы должны использовать ее, когда у нас есть только одна ветвь, и это предпочтительнее, как в этом случае.

В любом случае, это точно то же самое.

Ответ №3:

Я думаю, что это лошади для курсов… Я работал с очень хорошими разработчиками, чей родной язык не английский, и они находят If ! (если нет) более легким для понимания.

Но руководство по стилю Ruby https://github.com/bbatsov/ruby-style-guide определенно предпочитает unless if ! , но не одобряет unless использование с else .

Наконец, лучше переписать как однострочный с завершающим условным выражением…

     return '' unless dna_structure.include? strand