Combinatory method like tap, but able to return a different value?

假装没事ソ 提交于 2019-11-28 21:00:45

问题


I'm going through a phase of trying to avoid temporary variables and over-use of conditional where I can use a more fluid style of coding. I've taken a great liking to using #tap in places where I want to get the value I need to return, but do something with it before I return it.

def fluid_method
  something_complicated(a, b, c).tap do |obj|
    obj.update(:x => y)
  end
end

Vs. the procedural:

def non_fluid_method
  obj = something_complicated(a, b, c)
  obj.update(:x => y)
  obj # <= I don't like this, if it's avoidable
end

Obviously the above examples are simple, but this is a pretty common coding style in the ruby community nonetheless. I'll sometimes use #inject to pass an object through a series of filters too:

things.inject(whatever) do |obj, thing|
  thing.filter(obj)
end

Vs. the procedural:

obj = whatever
things.each do |thing|
  obj = thing.filter(obj)
end
obj

Now I'm facing repeated use of a condition like the following, and looking for a more fluid approach to handling it:

def not_nice_method
  obj = something_complex(a, b, c)
  if a_predicate_check?
    obj.one_more_method_call
  else
    obj
  end
end

The (slightly) cleaner solution is to avoid the temporary variable at the cost of duplication:

def not_nice_method
  if a_predicate_check?
    something_complex(a, b, c).one_more_method_call
  else
    something_complex(a, b, c)
  end
end

I can't help but feeling the desire to use something almost like #tap here though.

What other patterns might I follow here. I realise this is all just nonsensical sugar to some people and that I should just move onto more interesting problems, but I'm trying to learn to write in a more functional style, so I'm just curious what long-term rubyists have determined to be good ways to tackle situations like this. These examples are hugely simplified.


回答1:


Define Object#as:

class Object
  def as
    yield self
  end
end

And now you can write:

def not_sure_this_is_nice_enough_method1
  something_complex(a, b, c).as do |obj| 
    a_predicate_check? ? obj.one_more_method_call : obj
  end
end



回答2:


def best_nice_method
  something_complex(a, b, c).tap |obj|
    break obj.one_more_method_call if a_predicate_check?
  end
end

The magic is break in tap returns another value.

new

ruby 2.5 has yield_self which exactly you want. https://stackoverflow.com/a/47890832/683157




回答3:


I found a method in the Facets gem that might be what you were looking for: Kernel#ergo

So your original method:

def not_nice_method
  obj = something_complex(a, b, c)
  if a_predicate_check?
    obj.one_more_method_call
  else
    obj
  end
end

might end up looking something like this:

require 'facets/kernel/ergo'

def nice_method
  something_complex(a, b, c).ergo do |_| 
    a_predicate_check? ? _.one_more_method_call : _
  end
end



回答4:


I needed to do something like this and I like tokland's answer, but I didn't want to pollute Object for the small script I was writing. Instead, I made use of tap on an array:

[something_complicated].tap { |s| s[0] = new_cool_thing)}.first



回答5:


instance_eval can be misused for this purpose

"this".instance_eval { |test| test + " works" }

since 2.5 it is possible to use yield_self

"easy".yield_self{ |a| a + " peasy" }

Read more:

https://ruby-doc.org/core-1.9.3/BasicObject.html#method-i-instance_eval

https://ruby-doc.org/core-2.5.0/Object.html#method-i-yield_self



来源:https://stackoverflow.com/questions/7878687/combinatory-method-like-tap-but-able-to-return-a-different-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!