Ruby: вложенные операторы if

Я писал код, и он оказался слишком уродливым, на мой вкус. Можно ли как-то реорганизовать его, чтобы не использовать вложенные операторы if?

def hours_occupied(date)
  #assuming date is a valid date object    
  availability = get_work_hours(date)
  focus = "work"

  if availability.nil
    availability = get_family_hours(date)
    focus = "family"

    if availability.nil
      availability = get_friend_hours(date)
      focus = "friends"
    end
  end
end

Я знаю, что смогу сделать что-то подобное для доступности

availability = get_work_hours(date) || get_family_hours(date) || get_friend_hours(date)

но как мне соответственно установить переменную фокуса?


person rlhh    schedule 19.06.2013    source источник
comment
Можем ли мы предположить, что вы не показываете полный метод? в противном случае нет смысла назначать эти неиспользуемые переменные. В этом случае добавьте ... в качестве заполнителя внизу метода.   -  person tokland    schedule 19.06.2013
comment
@tokland: Я совершенно не в состоянии понять тему и цель его кода, поэтому я не могу ответить.   -  person Boris Stitnicky    schedule 19.06.2013


Ответы (4)


Еще один способ - просто переназначить значения, если в этом есть необходимость:

def hours_occupied(date)
  availability, focus = get_work_hours(date), "work"
  availability, focus = get_family_hours(date), "family" unless availability
  availability, focus = get_friend_hours(date), "friend" unless availability
end

или используя итератор:

def hours_occupied(date)
  availability = focus = nil
  %w(work family friend).each {|type| availability, focus = self.send(:"get_#{type}_hours", date), type unless availability}
end
person trushkevich    schedule 19.06.2013

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

def hours_occupied(date)
  if availability = get_work_hours(date)
    focus = "work"
  elsif availability = get_family_hours(date)
    focus = "family"
  elsif availability = get_friend_hours(date)
    focus = "friends"
  end
end
person Don Cruickshank    schedule 19.06.2013
comment
Условные выражения Ruby - это выражения, поэтому не нужно повторять focus 3 раза - person tokland; 19.06.2013

Я бы написал:

def hours_occupied(date)
  focus = if (availability = get_work_hours(date))
    "work"
  elsif (availability = get_family_hours(date))
    "family"
  elsif (availability = get_friend_hours(date))
    "friends"
  end
  # I guess there is more code here that uses availability and focus.
end

Однако я не уверен, что использование разных методов для разных типов - хорошая идея, так как это затрудняет написание кода. Другой подход с использованием Enumerable # map_detect:

focus, availability = [:work, :family, :friends].map_detect do |type|
  availability = get_hours(date, type)
  availability ? [type, availability] : nil
end
person tokland    schedule 19.06.2013
comment
Мне никогда не нравится видеть задания внутри условного теста. Это слишком похоже на C или Perl и вызывает у меня кошмары. Большое спасибо. :-) - person the Tin Man; 19.06.2013
comment
@theTinMan: Пожалуйста :-) Некоторые люди скажут, что если язык это позволяет (например, Python не позволяет), зачем этого избегать? разве вы не можете отличить == от =? Но я не буду :-) Мне нравится эта конструкция, потому что она упрощает код с меньшим количеством вложенных выражений. Обратите внимание, что скобки делают его более заметным. - person tokland; 19.06.2013

случай, когда также возможен:

focus = case availability
when get_work_hours(date)
  "work"
when get_family_hours(date)
  "family"
when get_friend_hours(date)
  "friends"
end
person steenslag    schedule 19.06.2013
comment
либо это неправильно, либо case работает не так, как я думал :-) - person tokland; 19.06.2013
comment
но availability должен содержать первый не равный нулю результат вызова метода, он заранее не известен. - person tokland; 19.06.2013
comment
Просто используйте case, а не case availability - person Stefan; 19.06.2013
comment
Ребята, немного озадачен вашими комментариями, проверьте вопрос, availability не известен! или я что-то упускаю? - person tokland; 19.06.2013
comment
@tokland, поэтому вы должны использовать case без параметра: focus = case when get_work_hours... - person Stefan; 19.06.2013
comment
Да, Стефан, но с присваиванием в каждом when, поэтому разве это не эквивалентно цепочке if? - person tokland; 19.06.2013