Ruby 以编程方式调用方法,参数数量可变
Ruby programmatically calling method, with variable number of args
我正在尝试做类似的事情:
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] 等,我可以做到这一点,但这看起来很浪费。
有没有一种方法可以在不编写硬编码为参数数量的代码的情况下实现这一点?
试试这个
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 运算符 *
打包或解包数组。
几年前我做了你现在正在尝试的事情。在方法参数前加上星号,您可以在函数中接收任意数量的参数。所以你不需要知道给定参数的数量。它被称为 splat。
也可以将您的值作为前面带有星号的数组发送,它会起作用。
我用 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
首先,你不应该使用 send
。如果您知道方法名称,则可以使用 public_send
、Method#call
或仅使用 bar(...)
。
同质参数
如果参数是同类的(例如,是同一个 Class 的实例),你可以将它们放在一个数组中,并使用这个数组作为参数:
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]
我正在尝试做类似的事情:
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] 等,我可以做到这一点,但这看起来很浪费。
有没有一种方法可以在不编写硬编码为参数数量的代码的情况下实现这一点?
试试这个
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 运算符 *
打包或解包数组。
几年前我做了你现在正在尝试的事情。在方法参数前加上星号,您可以在函数中接收任意数量的参数。所以你不需要知道给定参数的数量。它被称为 splat。
也可以将您的值作为前面带有星号的数组发送,它会起作用。
我用 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
首先,你不应该使用 send
。如果您知道方法名称,则可以使用 public_send
、Method#call
或仅使用 bar(...)
。
同质参数
如果参数是同类的(例如,是同一个 Class 的实例),你可以将它们放在一个数组中,并使用这个数组作为参数:
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]