'How to pre-fill arguments to a function in Ruby?
Let's say I have a function foo:
def foo(A, B, C)
A + B + C
end
And I call it like this, with only the last parameter changing:
foo("foo", "bar", "123")
foo("foo", "bar", "456")
foo("foo", "bar", "789")
How can I "bake" or "pre-fill" the arguments that do not change? So maybe I would get a new callable foo_baked
such that foo_baked("123")
is the same as foo("foo", "bar", "123")
?
And use it like this:
foo_baked = ...?
foo_baked("123")
foo_baked("456")
foo_baked("789")
Note that I do not want to define a new function using def
but want to be able to create foo_baked at runtime on the fly, maybe for an array.
Solution 1:[1]
Ruby has function-currying built in with the curry method:
def foo(a, b, c)
a + b + c
end
foo_baked = method(:foo).curry.call('foo', 'bar')
# this returns "foobar123"
foo_baked.call('123')
# ArgumentError (wrong number of arguments (given 4, expected 3))
foo_baked.call('123', '234')
Basically, Method#curry
returns a curried proc: a function, pre-loaded with arguments, that will only execute once enough arguments have been passed to satisfy the method signature.
Note that foo_baked.lambda?
returns true
, so a curried proc is actually a lambda. This is important since it means that you'll get an ArgumentError
if you exceed the maximum number of arguments.
You can allow additional arguments beyond the required three by passing an arity argument to curry
.
def foo(*args)
args.join
end
# does not execute since only 2 arguments have been supplied
foo_baked = method(:foo).curry(3).call('foo', 'bar')
# executes on the third argument
# this returns "foobar123"
foo_baked.call('123')
# this returns "foobar123234"
foo_baked.call('123', '234')
Solution 2:[2]
You can define a new method foo_baked
that wraps foo
and passes the non-changeable arguments as hardcoded values:
def foo_baked(c)
foo("foo", "bar", c)
end
Then it can be called like so:
foo_baked("123")
Alternatively, you can use Ruby's built-in #curry
method, as outlined in TonyArra's answer. This approach is more flexible, returning a callable proc until enough arguments are provided.
Solution 3:[3]
You have a few options. The first is a currying approach as outlined in Zoran's answer.
The second is to use default positional arguments. But in order to do this, the optional arguments need to be after the required ones:
def foo_baked(a, b="foo", c="bar")
[a, b, c]
end
foo_baked(123) # => [123, "foo", "bar"]
foo_baked(123, "asd")
The third option is to use keyword arguments. The benefit of this approach is that the arguments can be in any order. You can pick and choose which ones you provide values for.
def foo_baked(b: "foo", c: "bar", a:)
[a, b, c]
end
foo_baked(a: 123) # => [123, "foo", "bar"]
foo_baked(a: 123, b: "asd") # => [123, "asd", "bar"]
A final option would be to combine positional and keyword arguments:
def foo_baked(a, b: "foo", c: "bar")
[a, b, c]
end
foo_baked(123) # => [123, "foo", "bar"]
foo_baked(123, b: "asd") # => [123, "asd", "bar"]
This has the benefit of still letting you use the positional arguments, but the order of the optional args doesn't matter and they will never interfere with the positional ones
Solution 4:[4]
You could create a lambda wrapping original method:
foo_baked = ->(value) { foo('foo', 'bar', value) }
foo_baked.(123) # => [123, "foo", "bar"]
Note additional .
in the execution though. This is ruby special syntax, equivalent to:
foo_baked.call(123)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 | |
Solution 4 | BroiSatse |