Как элегантно вернуть 1.0, если возвращаемое значение равно 0 в 1 строке? - PullRequest
3 голосов
/ 24 марта 2020

У меня есть следующее утверждение:

> zv.sum(:volume).to_f || 1.0
   (0.5ms)  SELECT SUM("positions"."volume") FROM "positions" WHERE "positions"."volume" = $1  [["volume", 0]]
=> 0.0

Я хотел бы, чтобы, если zv.sum(:volume) вернул 0.0, я хотел бы, чтобы он возвратил 1.0.

I знаю, что я мог бы сделать подробное if / unless утверждение, но я хотел бы сделать это элегантно в 1 строке выше.

Как мне это сделать?

Ответы [ 3 ]

5 голосов
/ 24 марта 2020

Вы можете использовать nonzero? для обработки нуля как значения фальси:

zv.sum(:volume).to_f.nonzero? || 1.0

или:

(zv.sum(:volume).nonzero? || 1).to_f
5 голосов
/ 24 марта 2020

У вас буквально есть решение в вашем названии и в вашем вопросе ( жирный выделение мое):

Как элегантно вернуть 1,0 , если Возвращаемое значение равно 0, в 1 строке?

То, что я хотел бы получить, это , если zv.sum(:volume) возвращает 0.0, я хочу, чтобы оно было return 1.0 вместо этого.

Итак, лучшим способом было бы использовать условное выражение :

sum = zv.sum(:volume).to_f

if sum == 0.0
  1.0
else
  sum
end

Если вы хотите «оптимизировать» это, вы может переместить преобразование с плавающей точкой на самый край выражения:

sum = zv.sum(:volume)

if sum == 0
  1
else
  sum
end.to_f

[Обратите внимание, это предполагает, что значение zv.sum(:volume) будет числом, а не чем-то вроде nil или строкой, представляющей число. Если это гарантировано, то же преобразование может быть применено к каждому из следующих примеров.]

Требование сделать что-либо в 1 строке: всегда тривиально достижимо в Ruby , так как разрывы строк всегда необязательны. Например:

sum = zv.sum(:volume).to_f; if sum == 0.0 then 1.0 else sum end

Или, если вы хотите избежать использования ключевых слов:

sum = zv.sum(:volume).to_f; if sum == 0.0; 1.0 else sum end

Существует также условный оператор , но лично я не вижу смысла для него это также не может (и лучше) обслуживаться условным выражением:

sum = zv.sum(:volume).to_f; sum == 0.0 ? 1.0 : sum

Если вы хотите одно выражение, вы можете встроить переменную в условное выражение:

if (sum = zv.sum(:volume).to_f) == 0.0
  1.0
else
  sum
end

И, конечно, вы можете комбинировать два:

if (sum = zv.sum(:volume).to_f) == 0.0 then 1.0 else sum end

И с избеганием ключевых слов:

if (sum = zv.sum(:volume).to_f) == 0.0; 1.0 else sum end

И с (все еще бесполезным) условным оператором:

(sum = zv.sum(:volume).to_f) == 0.0 ? 1.0 : sum

Если вы не возражаете против некоторого слегка сложного нелокального потока управления, вы можете сделать что-то вроде этого:

zv.sum(:volume).to_f.tap {|sum| break 1.0 if sum == 0.0 }

Вытащить преобразование с плавающей точкой:

zv.sum(:volume).tap {|sum| break 1 if sum == 0 }.to_f

Также Если вы знаете что-то особенное о сумме, например, если вы знаете, что сумма всегда будет либо точно 0, либо больше 1, то вы можете использовать это дополнительное знание и, возможно, сделать что-то вроде этого:

[1.0, zv.sum(:volume).to_f].max

Вытаскивание поплавка: * 106 6 *

[1, zv.sum(:volume)].max.to_f

Тем не менее, обратите внимание, что ни один из этих не так удобочитаем и понятен, как первая версия.

Также обратите внимание, что описание в вашем вопросе и код в вашем вопросе не совпадает, так как код не соответствует тому, что вы описали в вопросе.

1 голос
/ 24 марта 2020

Я обычно предпочитаю что-то многословное и хорошо читаемое, например:

def some_method
  volume = zv.sum(:volume).to_f
  return 1.0 if volume.zero? 

  volume
end

, но если вы действительно хотите использовать однострочник, вы можете сделать что-то подобное:

zv.sum(:volume).to_f.yield_self { |r| r.zero? ? 1.0 : r }

...