Ruby以编程方式调用方法,参数数目可变

w3nuxt5m  于 2023-04-29  发布在  Ruby
关注(0)|答案(3)|浏览(136)

我正在尝试做类似的事情:

def foo(mode= :serial)

  if (mode == :serial) then
    self.send(:bar, "one_type")
  else
    self.send(:bar,"second_type",:T5)
  end
end

我当然可以这样打出来。
但我最近尝试扩展它以包含第二个函数,如下所示:

def foo(mode= :serial)

  if (mode == :serial) then
    self.send(:bar, "one_type")
    self.send(:foobar, "one_type",20)
  else
    self.send(:bar,"second_type",:T5)
    self.send(:foobar, "one_type",20,:T5)
  end
end

我仍然可以继续,但我对自己说,这里有一个模式,我应该把论点抽象到另一个层面,让它更简单。
所以我想做的事情是这样的:

arg_arr   = [:bar, "one_type"]
arg_arr_2 = [:foobar, "one_type", 20]
if (mode == :serial) then
  self.send(arg_arr)
  self.send(arg_arr_2)
else
  arg_arr << :T5
  arg_arr2 << :T5
  self.send(arg_arr)
  self.send(arg_arr2 )
end

我尝试了一些其他的想法。each,.inspect,但没有什么可以工作的(通常的错误是不能将数组转换为字符串,我猜这是指它将数组视为整个函数名的事实)。如果我显式地说“使用数组元素[0],[1]等,我就可以做到这一点,但这似乎很浪费。
有没有一种方法可以实现这一点,而不必编写硬编码到参数数量的代码?

ffx8fchx

ffx8fchx1#

试试这个

def foo(a, b)
  puts a
  puts b
end

array = ['bar', 'qux']
send(:foo, *array) # using send
foo(*array) # using the method name

都打印

bar
qux

splat运算符*对数组进行打包或解包。

ccrfmcuu

ccrfmcuu2#

几年前我做了你现在正在尝试的事情。在方法参数前面加上星号,你可以在函数中接收任意多的参数。所以你不需要知道给定参数的个数。这叫劈啪声
将你的值作为一个前面有星号的数组发送,它也会工作。
我用一个irb控制台测试了以下内容:

def test(*args)
  puts args.inspect
end

my_args = [1, 2, 3]
self.send(:test, *my_args)
# [1, 2, 3]
# => nil

或者发送任意数量的单个参数:

self.send(:test, 'a', 'b', 'c', 'd')
# ["a", "b", "c", "d"]
# => nil

如果您有固定数量的参数,这将工作:

def test(arg1, arg2, arg3)
  puts arg1.inspect
  puts arg2.inspect
  puts arg3.inspect
end

my_args = [1, 2, 3]
self.send(:test, *my_args)
# 1
# 2
# 3
# => nil
vu8f3i0k

vu8f3i0k3#

首先,你不应该使用send。你可以使用public_sendMethod#call或者仅仅是bar(...),如果你知道方法名的话。

齐次参数

如果参数是均匀的(例如例如,它们是同一个类的示例),你可以把它们放在一个数组中,并使用这个数组作为参数:

def analyze_array(array)
  puts "Elements : #{array}"
  puts "Length   : #{array.size}"
  puts "Sum      : #{array.inject(:+)}"
  puts
end

analyze_array([1,2,3])
analyze_array([1,2,3,4,5])

它输出:

Elements : [1, 2, 3]
Length   : 3
Sum      : 6

Elements : [1, 2, 3, 4, 5]
Length   : 5
Sum      : 15

示例

稍微重构一下你的代码,它可能会变成:

arg_arr   = [:bar, 1]
arg_arr_2 = [:foobar, 1, 2]

def bar(array)
  puts "  bar with one parameter : #{array}"
end

def foobar(array)
  puts "  foobar with one parameter : #{array}"
end

[:serial, :parallel].each do |mode|
  puts "Mode : #{mode}"
  [arg_arr, arg_arr_2].each do |method_and_args|
    method_name, *args = method_and_args
    args << 3 if mode != :serial
    method(method_name).call(args)
  end
end

它输出:

Mode : serial
  bar with one parameter : [1]
  foobar with one parameter : [1, 2]
Mode : parallel
  bar with one parameter : [1, 3]
  foobar with one parameter : [1, 2, 3]

异构参数

对于可能属于不同类的未知数量的参数,可以使用splat运算符(documentation):

def analyze_parameters(*params)
  puts "Parameters : #{params}"
  puts "Number     : #{params.size}"
  puts "Classes    : #{params.map(&:class)}"
end

analyze_parameters('Test')
analyze_parameters(1, 'a', :b, [:c, :d])

它输出:

Parameters : ["Test"]
Number     : 1
Classes    : [String]

Parameters : [1, "a", :b, [:c, :d]]
Number     : 4
Classes    : [Fixnum, String, Symbol, Array]

您的示例将变为:

arg_arr   = [:bar, 1 ]
arg_arr_2 = [:foobar, 1, 'a']

def bar(*params)
  puts "  bar with multiple parameters : #{params}"
end

def foobar(*params)
  puts "  foobar with multiple parameters : #{params}"
end

[:serial, :parallel].each do |mode|
  puts "Mode : #{mode}"
  [arg_arr, arg_arr_2].each do |method_and_args|
    method_name, *args = method_and_args
    args << :b if mode != :serial
    method(method_name).call(*args)
  end
end

它输出:

Mode : serial
  bar with multiple parameters : [1]
  foobar with multiple parameters : [1, "a"]
Mode : parallel
  bar with multiple parameters : [1, :b]
  foobar with multiple parameters : [1, "a", :b]

相关问题