Home > Blockchain >  How to pre-fill arguments to a function in Ruby?
How to pre-fill arguments to a function in Ruby?

Time:04-22

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")

CodePudding user response:

A couple of answers/comments mentioned currying, but none of them used the curry method in Ruby, which exists:

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 that will only execute once enough arguments have been passed to it 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

foo_baked = method(:foo).curry(3).call('foo', 'bar')

# this returns "foobar123"
foo_baked.call('123')

# this returns "foobar123234"
foo_baked.call('123', '234')

CodePudding user response:

You can define a new method foo_baked that wraps foo and passes the non-changeable arguments as defaults, like so:

def foo_baked(c)
  foo("foo", "bar", c)
end

You can then use it like so:

foo_baked("123")

CodePudding user response:

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

CodePudding user response:

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)
  • Related