是否可以将参数绑定到(一元)Proc对象?

时间:2016-12-13 08:55:11

标签: ruby

(Crossposting note:我已经在一周前的Ruby Forum已经问过这个问题了,但是还没有得到任何答复。)

这是我目前所用的(非常)简化的工作版本:

u = new Vector(1.0, 0.0, 0);

在这个例子中,根据T的构造方式,T#调用调用 S#s_method1或S#S_method2,但是在调用的情况下 S#s_method1,s_method1的参数在创建时已经修复 T对象的时间。因此,以下两行,

import MySQLdb
conn = MySQLdb.connect(host = 'localhost', user = 'username', passwd = 'password', port = port, db = 'DBNAME')
cursor = conn.cursor()
query = """SELECT * FROM myschema.mytable;"""
cursor.execute(query)

FILE = cursor.fetchall()

with open('FILE.txt', 'w') as f:
    for row in cursor:
    f.write("%s\n" % str(row))

产生输出

# 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.new('no arguments').invoke T.new('one argument', 12345).invoke nil 的情况下,即我要调用的地方 无参数方法s_method2 no arguments s_method1 12345 one argument ,我可以得到我的可调用对象

的优雅方式
choice

s_method2 nil 的情况下,我必须构造一个@s.method(:s_method2) 对象 使用`lambda。这不仅看起来笨拙,而且让我感觉有点 不舒服。我们这里有一个关闭,它连接到 初始化方法里面的环境,我不确定是否这样 在某些情况下可能会因内存泄漏而导致麻烦。

是否可以轻松绑定方法对象(在本例中) choice到固定参数?

我的第一个想法是使用

Proc

但这并没有实现我的目标。它不会返回可调用的@s.method(:s_method1)对象,而是实际执行@s.method(:s_method1).curry[choice] (这不是错误,而是记录的行为)。

关于如何实现目标的任何其他想法?

1 个答案:

答案 0 :(得分: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

默认参数的方法改进(Ruby 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

Monkey-Patching Method类(Ruby 1.9 +)

如果修补Method类是可以接受的,可以使用:

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

Wrapping Method类(Ruby 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

欢迎任何评论或建议!