Ruby:在将proc转换为块时提供参数

Dre*_*ker 8 ruby

我们可以轻松定义一个方法,并将其转换为带有一元&符号的块.

def my_method(arg)
  puts arg*2
end

['foo', 'bar'].each(&method(:my_method))

# foofoo
# barbar

# or
my_method = ->(arg) { puts arg*2 }
['foo', 'bar'].each(&my_method)
# same output
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的,当我们使用聚合时,第一个参数会自动传递.但是,如果我们需要传递2个甚至更多的参数呢?

my_method = ->(arg,num) { puts arg*num }
['foo', 'bar'].each(&my_method)
# ArgumentError: wrong number of arguments (1 for 2)
['foo', 'bar'].each(&my_method(3))
# NoMethodError: undefined method `foo' for main:Object
['foo','bar'].each do |i, &my_method|
  yield i, 3
end
# LocalJumpError: no block given (yield)
Run Code Online (Sandbox Code Playgroud)

在将proc转换为块时,是否可以传递其他参数?

fyl*_*ooi 6

@sawa是对的.你可以这样做curry.

Proc版本:

mult = proc {|a, b| a * b} # => #<Proc:0x00000002af1098@(irb):32>
[1, 2].map(&mult.curry[2])  # => [2, 4]
Run Code Online (Sandbox Code Playgroud)

方法版本:

def mult(a, b)
  a*b
end

[1, 2].map(&method(:mult).to_proc.curry[2])  # => [2, 4]
Run Code Online (Sandbox Code Playgroud)

  • 您的最后一行引用了第一行中分配的局部变量`mult`,而不是方法. (2认同)
  • 您可以使用`[1,2] .map(&method(:mult).to_proc.curry [2])`,虽然它看起来很麻烦. (2认同)
  • @DreamWalker讨论"修复"第一个参数,而不是最后一个,即`proc.curry [a] [b]`相当于`proc [a,b]` (2认同)

Ste*_*fan 1

关于您的评论

奇怪,但它在表演过程中交换了参数

实际上,参数顺序保留的。

curry返回一个新的过程,该过程有效地收集参数,直到有足够的参数来调用原始方法/过程(基于其数量)。这是通过返回中间过程来实现的:

def foo(a, b, c)
  { a: a, b: b, c: c }
end

curried_proc = foo.curry  #=> #<Proc:0x007fd09b84e018 (lambda)>
curried_proc[1]           #=> #<Proc:0x007fd09b83e320 (lambda)>
curried_proc[1][2]        #=> #<Proc:0x007fd09b82cfd0 (lambda)>
curried_proc[1][2][3]     #=> {:a=>1, :b=>2, :c=>3}
Run Code Online (Sandbox Code Playgroud)

您可以一次将任意数量的参数传递给柯里化过程:

curried_proc[1][2][3]     #=> {:a=>1, :b=>2, :c=>3}
curried_proc[1, 2][3]     #=> {:a=>1, :b=>2, :c=>3}
curried_proc[1][2, 3]     #=> {:a=>1, :b=>2, :c=>3}
curried_proc[1, 2, 3]     #=> {:a=>1, :b=>2, :c=>3}
Run Code Online (Sandbox Code Playgroud)

空参数将被忽略:

curried_proc[1][][2][][3] #=> {:a=>1, :b=>2, :c=>3}
Run Code Online (Sandbox Code Playgroud)

但是,您显然无法更改参数顺序。


柯里化的另一种选择是部分应用,它通过修复一个或多个参数返回一个具有较低数量的新过程。与 不同curry,没有用于部分应用的内置方法,但您可以轻松编写自己的方法:

my_proc = -> (arg, num) { arg * num }

def fix_first(proc, arg)
  -> (*args) { proc[arg, *args] }
end

fixed_proc = fix_first(my_proc, 'foo')  #=> #<Proc:0x007fa31c2070d0 (lambda)>
fixed_proc[2]  #=> "foofoo"
fixed_proc[3]  #=> "foofoofoo"

[2, 3].map(&fixed_proc) #=> ["foofoo", "foofoofoo"]
Run Code Online (Sandbox Code Playgroud)

或者修复最后一个参数:

def fix_last(proc, arg)
  -> (*args) { proc[*args, arg] }
end

fixed_proc = fix_last(my_proc, 2)  #=> #<Proc:0x007fa31c2070d0 (lambda)>
fixed_proc['foo']  #=> "foofoo"
fixed_proc['bar']  #=> "barbar"

['foo', 'bar'].map(&fixed_proc) #=> ["foofoo", "barbar"]
Run Code Online (Sandbox Code Playgroud)

当然,您不仅限于修复单个参数。例如,您可以返回一个接受数组并将其转换为参数列表的过程:

def splat_args(proc)
  -> (array) { proc[*array] }
end

splatting_proc = splat_args(my_proc)
[['foo', 1], ['bar', 2], ['baz', 3]].map(&splatting_proc)
#=> ["foo", "barbar", "bazbazbaz"]
Run Code Online (Sandbox Code Playgroud)