2016-12-13 70 views
3

(Crossposting注:我已在Ruby Forum一週前問這個了,但沒有得到任何迴應還)。是否可以將參數綁定到(一元)Proc對象?

這裏是一個(非常)簡單,工作是我到目前爲止版本:

# A class S with two methods, one which requires one parameter, and 
# one without parameters. 
class S 
    def initialize(s); @ms = s; end 
    def s_method1(i); puts "s_method1 #{i} #{@ms}"; end 
    def s_method2; puts "s_method2 #{@ms}"; end 
end 

# A class T which uses S, and "associates" itself to 
# one of the both methods in S, depending on how it is 
# initialized. 
class T 
    def initialize(s, choice=nil) 
    @s = S.new(s) 
    # If choice is true, associate to the one-parameter-method, otherwise 
    # to the parameterless method. 
    @pobj = choice ? lambda { @s.s_method1(choice) } : @s.method(:s_method2) 
    end 

    # Here is how I use this association 
    def invoke 
    @pobj.call 
    end 
end 

在這個例子中,依賴於T是如何構建的,T#調用調用 既無S#s_method1或S#S_method2,但在主叫 小號#s_method1的情況下,將參數s_method1已經是固定在T對象的創建時間 。因此,下面兩行,

T.new('no arguments').invoke 
T.new('one argument', 12345).invoke 

產生輸出

s_method2 no arguments 
s_method1 12345 one argument 

這正是我需要的。

現在我的問題:

在這種情況下,當choice,即在這裏我要調用 參數方法s_method2,我可以讓我調用對象在 優雅的方式通過

@s.method(:s_method2) 

choice是非的情況下,我不得不CONSTRU ct a Proc object using`lambda。這不僅看起來笨拙,而且讓我感覺有點不舒服。我們這裏有一個封閉,連接到初始化方法裏面 環境,我不知道這是否 可以通過在某些情況下導致內存泄漏引起麻煩。

有沒有簡單地 @s.method(:s_method1)結合的方法對象(在這種情況下,以一個固定參數的簡單方法?

我的第一個想法是使用

@s.method(:s_method1).curry[choice] 

,但這並沒有達到我的目標。它不會返回調用Proc對象,而是實際執行s_method1(這是不是一個錯誤,但記錄的行爲)。

的我的目標如何才能實現任何其他的想法?

+0

你的拉姆達在那裏,它__是一個窮人的咖喱。 –

+0

我知道了,我也希望有一些有錢的人可以用currying獲得;-) – user1934428

+1

除了我的頭頂之外,我認爲這是最好的你可以得到紅寶石。 –

回答

3

保存參數分別

此選項很簡單,但它可能不是你要找的內容:

class T 
    def initialize(s, choice=nil) 
    s = S.new(s) 
    @choice = choice 
    @pobj = s.method(choice ? :s_method1 : :s_method2) 
    end 

    def invoke 
    @pobj.call(*@choice) 
    end 
end 

T.new('no arguments').invoke 
T.new('one argument', 12345).invoke 
#=> s_method2 no arguments 
#=> s_method1 12345 one argument 

默認參數方法的改進(紅寶石2。0+)

# Allows setting default parameters for methods, after they have been defined. 
module BindParameters 
    refine Method do 
    def default_parameters=(params) 
     @default_params = params 
    end 

    def default_parameters 
     @default_params || [] 
    end 

    alias_method :orig_call, :call 

    def call(*params) 
     merged_params = params + (default_parameters[params.size..-1] || []) 
     orig_call(*merged_params) 
    end 
    end 
end 

下面是一個例子:

def f(string) 
    puts "Hello #{string}" 
end 

def g(a, b) 
    puts "#{a} #{b}" 
end 

using BindParameters 

f_method = method(:f) 
f_method.default_parameters = %w(World) 

f_method.call('user') # => Hello user 
f_method.call   # => Hello World 

g_method = method(:g) 
g_method.default_parameters = %w(Hello World) 

g_method.call     # => Hello World 
g_method.call('Goodbye')   # => Goodbye World 
g_method.call('Goodbye', 'User') # => Goodbye User 

您的代碼可以被改寫:

class T 
    using BindParameters 
    def initialize(s, *choice) 
    s = S.new(s) 
    @pobj = s.method(choice.empty? ? :s_method2 : :s_method1) 
    @pobj.default_parameters = choice 
    end 

    def invoke 
    @pobj.call 
    end 
end 

T.new('no arguments').invoke   # => s_method2 no arguments 
T.new('one argument', 12_345).invoke # => s_method1 12345 one argument 

猴拼圖法類(紅寶石1.9+)

如果可以使用以下方法:

class Method 
    def default_parameters=(params) 
    @default_params = params 
    end 

    def default_parameters 
    @default_params || [] 
    end 

    alias_method :orig_call, :call 

    def call(*params) 
    merged_params = params + (default_parameters[params.size..-1] || []) 
    orig_call(*merged_params) 
    end 
end 

T爲:

class T 
    def initialize(s, *choice) 
    s = S.new(s) 
    @pobj = s.method(choice.empty? ? :s_method2 : :s_method1) 
    @pobj.default_parameters = choice 
    end 

    def invoke 
    @pobj.call 
    end 
end 

包裝方法類(紅寶石1.9+)

這種方法可能是更清潔,如果你不想污染的方法類:

class MethodWithDefaultParameters 
    attr_accessor :default_parameters 
    attr_reader :method 

    def initialize(receiver, method_symbol) 
    @method = receiver.public_send(:method, method_symbol) 
    @default_parameters = [] 
    end 

    def call(*params) 
    merged_params = params + (default_parameters[params.size..-1] || []) 
    method.call(*merged_params) 
    end 

    def method_missing(sym, *args) 
    method.send(sym, *args) 
    end 
end 

T變爲:

class T 
    def initialize(s, *choice) 
    s = S.new(s) 
    @pobj = MethodWithDefaultParameters.new(s, choice.empty? ? :s_method2 : :s_method1) 
    @pobj.default_parameters = choice 
    end 

    def invoke 
    @pobj.call 
    end 
end 

歡迎任何評論或建議!

+0

我真的很喜歡你用BindParameters的方法(儘管它現在不適用於我,因爲我在Ruby 1.9.3上,它沒有'refine'和'using'')。雖然這對目前的問題是一種矯枉過正,但我​​想我也可以在其他情況下使用它。 – user1934428

+1

請隨時在您的下一個問題中提及任何Ruby版本要求;)我更新了適用於Ruby 1.9的兩個腳本的答案。 –

+0

非常感謝!我最喜歡你的'MethodWithDefaultParameters',因爲它具有與其他類最乾淨的分離! – user1934428

相關問題