开发者

Ruby "return unless nil" idiom

I've got a smelly method like:

def search_record(*args)    
  record = expensive_operation_1(foo)
  return record unless record.nil?

  record = expensive_operation_2(foo, bar)
  return record unless record.nil?

  record = expensive_operation_3(baz)
  return record开发者_StackOverflow unless record.nil?

  record = expensive_operation_4(foo, baz)
  return record unless record.nil?
end

Is there a good ruby idiom for "return result of call unless nil"?

Or should I just write a return_unless_nil(&blk) method?

(Note that args are different for each call, so I can't simply just iterate over them)


Do you care about the difference between nil and false here? If you only care whether the return value of each method is "falsy," then this is a pretty Rubyish way of doing it:

def search_record(*args)    
  expensive_operation_1(foo)      ||
  expensive_operation_2(foo, bar) ||
  expensive_operation_3(baz)      ||
  expensive_operation_4(foo, baz)
end

If you're unfamiliar with this idiom, it can be explained thusly: Ruby, like most languages, "short circuits" OR comparisons, meaning that if the first operand evaluates to "truey" it won't bother to evaluate the second operand (i.e. if expensive_operation_1 returns something other than nil or false, it won't ever call expensive_operation_2), because it already knows that the result of the boolean operation is true.

Another useful thing that Ruby does is, instead of returning true or false from boolean operations it just returns the last operand it evaluates. So in this case if expensive_operation_1 returns nil, it will then call expensive_operation_2, and if that returns a value (that isn't falsy), the whole expression will just evaluate to that value.

Finally, we can chain these booleans so, in effect, it will return the result of the first operand that isn't falsy and never evaluate the subsequent operands. If all of the operands evaluate to falsy, it will return the final operand (which we know is falsy and, in your case, probably nil).


Complementing Jordan's answer: let's imagine these functions could return a boolean (unlikely for a search function, but anyway) and || does not work. We could then use the abstraction discussed here:

expensive_operation_1(foo).or_if(:nil?) do
  expensive_operation_2(foo).or_if(:nil?) do
    expensive_operation_3(foo).or_if(:nil?) do
      expensive_operation_4(foo)
    end
  end
end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜