2011-10-24 20 views
26

我正在經歷一個嘗試避免臨時變量和過度使用條件的階段,我可以使用更流暢的編碼風格。我非常喜歡在想要獲得我需要返回的值的地方使用#tap,但在返回之前先用它做點什麼。像點擊一樣的組合方法,但能夠返回不同的值?

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

比。程序:

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 

顯然上面的例子很簡單,但是這是在Ruby社區仍然一個很常見的編碼風格。我有時會使用#inject也通過一系列過濾器傳遞對象:

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

比。程序:

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

現在我面臨着重複使用類似下面的條件,並尋找一個更流暢的方法來處理它:

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

(略)清潔的解決方案是避免重複成本的臨時變量:

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 

我不禁感慨使用的東西幾乎喜歡的慾望#tap雖然。

我可以在這裏遵循什麼其他模式。我意識到這對一些人來說只是無意義的糖,我應該轉向更有趣的問題,但我正在努力學習以更實用的方式寫作,所以我只是好奇長期的紅寶石主義者決定了什麼成爲解決這種情況的好方法。這些例子非常簡單。

+3

冒着迂腐的風險,似乎你使用水龍頭誘導副作用是反功能的。功能程序員和語言避免或防止副作用。點擊的重點在於它不會返回在其中執行的內容。因此,可以使用兩種方法:調試和誘發副作用的方法。功能方法很簡單,就是將方法鏈接在一起或組合它們。 –

+0

沒有風險,我想談論理論,但是如果沒有直接的問題,我擔心這個線程會被關閉,但是,'#update'會返回一個布爾值,而不是'obj'的值(這超出了我的控制),不輕拍解決需要第三個表達式返回原始值?我想了解更多正確的功能技巧:) – d11wtq

+0

啊,我看到我可以改變:'update(something_complex(a,b,c))',我已經定義'update'來做'argument.update :x => y)'...雖然這會得到更詳細的更新參數需要傳入。 – d11wtq

回答

15

定義Object#as

class Object 
    def as 
    yield self 
    end 
end 

現在你可以這樣寫:

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

哦,你的'Object#as'技巧絕對比'assign'更好(在我眼中) if..else..end'版本。你可能有'some_method(a,b).on(a_predicate?){| obj | obj.whatever}也是。我可以把你的'as'並稍微反轉一下,給調用者添加'with':(something_complex(a,b)){| obj | a_predicate? obj.whatever:obj}'。嗯。 – d11wtq

+0

@ d11wtq:的確,on {}看起來也很好,通常最好是調用一個方法,而不是用send(:methods)包裝器隱藏它(問題是你必須給它起個名字)。注意你的with(x){| x | ...}實際上是Ick的讓:http://ck.rubyforge.org/ – tokland

+0

Ick看起來很有趣,但我懷疑它不會與RSpec發揮好,因爲它有一個它自己的'let'方法,表現完全不同:) – d11wtq

3

我發現了刻面寶石的方法,可能是你正在尋找:Kernel#ergo

所以您的原始方法:

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

最終可能看起來像這樣:

require 'facets/kernel/ergo' 

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

我需要做這樣的事情,我喜歡tokland的答案,但我不想污染對象的小腳本我寫。相反,我利用了tap陣列上:

[something_complicated].tap { |s| s[0] = new_cool_thing)}.first 
0
def best_nice_method 
    something_complex(a, b, c).tap |obj| 
    break obj.one_more_method_call if a_predicate_check? 
    end 
end 

神奇的是breaktap返回另一個值。

相關問題