Ruby: provide an argument while turning proc to a block - ruby

We can easily define a method and turn it into block with unary ampersand.
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
As we see the first argument is passed automatically when we work with aggregates. But what if we need to pass 2 or even more arguments?
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)
Is that possible to pass additional arguments while turning proc to a block?

#sawa is right. You can do that with curry.
Proc version:
mult = proc {|a, b| a * b} # => #<Proc:0x00000002af1098#(irb):32>
[1, 2].map(&mult.curry[2]) # => [2, 4]
Method version:
def mult(a, b)
a*b
end
[1, 2].map(&method(:mult).to_proc.curry[2]) # => [2, 4]

Regarding your comment:
Strange, but it swaps arguments during the performance
Actually, the argument order is preserved.
curry returns a new proc that effectively collects arguments until there are enough arguments to invoke the original method / proc (based on its arity). This is achieved by returning intermediate procs:
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}
You can pass any number of arguments at once to a curried proc:
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}
Empty arguments are ignored:
curried_proc[1][][2][][3] #=> {:a=>1, :b=>2, :c=>3}
However, you obviously can't alter the argument order.
An alternative to currying is partial application which returns a new proc with lower arity by fixing one or more arguments. Unlike curry, there's no built-in method for partial application, but you can easily write your own:
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"]
Or fixing the last argument:
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"]
Of course, you are not limited to fixing single arguments. You could for example return a proc that takes an array and converts it to an argument list:
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"]

Related

Mass method invocation

So in Ruby one can mass assign variables like this:
a, b, c = [1, 2, 3]
But what if I wanted to do the same for object methods, but without having to write out the whole thing like so:
foo.a, foo.b, foo.c = [1, 2, 3]
Is there a DRY way to accomplish this?
I am not sure you’ll like it, but the DRYest way I can think of is:
[:a, :b, :c].zip([1, 2, 3]).each { |k, v| foo.public_send "#{k}=", v }
# or vice versa
[1, 2, 3].zip([:a, :b, :c]).each { |v, k| foo.public_send "#{k}=", v }
Or, in more OO way:
class Foo
attr_accessor :a, :b, :c
def massive_assign attrs, values
attrs.zip(values).each { |k, v| public_send "#{k}=", v }
end
end
foo = Foo.new
foo.massive_assign([:a, :b, :c], [1, 2, 3])
Mass assignment to instance variables can be done like below as well:
foo.instance_eval { #a, #b, #c = [1, 2, 3] }
There's no specific syntax, but you could implement a setter for multiple attributes that are passed as a hash. Rails uses a similar approach:
class Foo
attr_accessor :a, :b, :c
def attributes=(attrs)
attrs.each do |name, value|
public_send("#{name}=", value)
end
end
end
foo = Foo.new
#=> #<Foo:0x007fc6d8a1e950>
foo.attributes = { a: 1, b: 2, c: 3 }
#=> #<Foo:0x007fc6d8a1e950 #a=1, #b=2, #c=3>
From the comments up there I assume that the poster is meaning a case where it's not actually foo but a more complex expression; and not only 3 assignments (method calls) but many.
There are 2 techniques that come to mind: tap and send:
some.complex[expression].which.evaluates.to.the.receiving.object.tap do |obj|
value_hash.each_pair do |key,value|
obj.send("#{key}=", value)
end
end
Hope that helps.
Of course, the usual security caveats apply (better make sure value_hash only contains valid/sane/allowed names).

Distinguish {k: :v} vs. [:k, :v] when iterating through Hash/Array

I need to implement the callback on #each. The receiver of each might be both Array and Hash. So, I have a code like:
receiver.each do |k, v|
case receiver
when Hash then puts "#{k} = #{v}"
when Array then puts "[#{k}, #{v}]"
end
end
The check for receiver is lame, though. Is there a way to receive/interprete a codeblock argument[s] to clearly distinguish the following cases:
{ a: 1, b: 2 }
versus
[[:a, 1], [:b, 2]]
I tried parenthesis, single argument, splatted argument. Everything just gets an Array of size 2. Am I doomed to stick with explicit type check?
The best you can do is to get the type check out of the loop:
def foo(receiver)
format = receiver.is_a?(Array) ? "[%s, %s]" : "%s = %s"
receiver.each do |k_v|
puts format % k_v
end
end
See String#%
If you want to tell, within the each block, whether the key/value pair came from an array or a hash, you have to resort to monkey patching. It can be done, but it's pretty awful:
class Hash
orig_each = instance_method(:each)
define_method(:each) do |&block|
orig_each.bind(self).call do |kv|
def kv.from_hash?
true
end
def kv.from_array?
false
end
block.call(kv)
end
end
end
class Array
orig_each = instance_method(:each)
define_method(:each) do |&block|
orig_each.bind(self).call do |kv|
def kv.from_hash?
false
end
def kv.from_array?
true
end
block.call(kv)
end
end
end
in use:
e = {a: 1, b: 2}
e.each do |kv|
p [kv.from_hash?, kv.from_array?, kv]
end
# => [true, false, [:a, 1]]
# => [true, false, [:b, 2]]
e = [[:a, 1], [:b, 2]]
e.each do |kv|
p [kv.from_hash?, kv.from_array?, kv]
end
# => [false, true, [:a, 1]]
# => [false, true, [:b, 2]]

Differences between [1,2,3].to_enum and [1,2,3].enum_for in Ruby

In Ruby I'm trying to understand between the to_enum and enum_for methods. Before I my question, I've provided some sample code and two examples to help w/ context.
Sample code:
# replicates group_by method on Array class
class Array
def group_by2(&input_block)
return self.enum_for(:group_by2) unless block_given?
hash = Hash.new {|h, k| h[k] = [] }
self.each { |e| hash[ input_block.call(e) ] << e }
hash
end
end
Example # 1:
irb (main)> puts [1,2,3].group_by2.inspect
=> #<Enumerator: [1, 2, 3]:group_by2>
In example #1: Calling group_by on the array [1,2,3], without passing in a block, returns an enumerator generated with the command self.enum_for(:group_by_2).
Example #2
irb (main)> puts [1,2,3].to_enum.inspect
=> #<Enumerator: [1, 2, 3]:each>
In example #2, the enumerator is generated by calling the to_enum method on the array [1,2,3]
Question:
Do the enumerators generates in examples 1 and 2, behave differently in any way? I can see from the inspected outputs that they show slightly different labels, but I can find any difference in the enumerators' behavior.
# Output for example #1
#<Enumerator: [1, 2, 3]:each> # label reads ":each"
# Output for example #2
#<Enumerator: [1, 2, 3]:group_by2> # label reads ":group_by2"
p [1, 2, 3].to_enum
p [1, 2, 3].enum_for
--output:--
#<Enumerator: [1, 2, 3]:each>
#<Enumerator: [1, 2, 3]:each>
From the docs:
to_enum
Creates a new Enumerator which will enumerate by calling method on
obj, passing args if any.
...
enum_for
Creates a new Enumerator which will enumerate by calling method on
obj, passing args if any.
ruby is a language that often has method names that are synonyms.
Followup question:
Does the symbol in the command [1,2,3].to_enum(:foo) serve a purpose,
other than replacing :each with :foo in the output?
Yes. By default, ruby hooks up the enumerator to the receiver's each() method. Some classes do not have an each() method, for instance String:
str = "hello\world"
e = str.to_enum
puts e.next
--output:--
1.rb:3:in `next': undefined method `each' for "helloworld":String (NoMethodError)
from 1.rb:3:in `<main>
to_enum() allows you to specify the method you would like the enumerator to use:
str = "hello\nworld"
e = str.to_enum(:each_line)
puts e.next
--output:--
hello
Now, suppose you have the array [1, 2, 3], and you want to to create an enumerator for your array. An array has an each() method, but instead of creating an enumerator with each(), which will return each of the elements in the array, then end; you want to create an enumerator that starts over from the beginning of the array once it reaches the end?
e = [1, 2, 3].to_enum(:cycle)
10.times do
puts e.next()
end
--output:--
1
2
3
1
2
3
1
2
3
1

Path to an embedded object

Given a nested array or hash as the receiver and some object as the argument, what is the best way to return the path to an occurrence of the object if the receiver includes the object, or nil otherwise? I define path as an array of array indices or hash keys that leads to the object. The argument object will never be any of the hash keys, and will never appear more than once. For example, I expect:
[
:a,
[:b, :c, {:d => :foo}],
:e,
]
.path_to(:foo) # => [1, 2, :d]
{
:a => [3, "foo"],
:b => 5,
:c => 2,
}
.path_to(3) # => [:a, 0]
When there is no occurrence, return nil:
[:foo, "hello", 3]
.path_to(:bar) => nil
If no one comes up with a reasonable answer, then I will post my own answer shortly.
Here you are my own recursive solution. I am sure that it could be improved but it is a good start and works exactly as requested.
# path.rb
module Patheable
def path_to item_to_find
path = []
find_path(self, item_to_find, path)
result = path.empty? ? nil : path
result.tap { |r| puts r.inspect } # just for testing
end
private
def find_path(current_item, item_to_find, result)
if current_item.is_a?(Array)
current_item.each_with_index do |value, index|
find_path(value, item_to_find, result.push(index))
end
elsif current_item.is_a?(Hash)
current_item.each do |key, value|
find_path(value, item_to_find, result.push(key))
end
else
result.pop unless current_item == item_to_find
end
end
end
class Array
include Patheable
end
class Hash
include Patheable
end
[
:a,
[:b, :c, {:d => :foo}],
:e,
].path_to(:foo) # => [1, 2, :d]
{
:a => [3, "foo"],
:b => 5,
:c => 2,
}.path_to(3) # => [:a, 0]
[:foo, "hello", 3].path_to(:bar) # => nil
#end path.rb
# example of use
$ ruby path.rb
[1, 2, :d]
[:a, 0]
nil
Nothing like a bit of recursion.
require 'minitest/autorun'
class Array
def path_to(obj)
# optimize this
Hash[self.each.with_index.to_a.map {|k,v| [v,k]}].path_to(obj)
end
end
class Hash
def path_to(obj)
inverted = self.invert
if inverted[obj]
[inverted[obj]]
else
self.map {|k, v|
if v.respond_to?(:path_to)
if res = v.path_to(obj)
[k] + res
end
end
}.find {|path|
path and path[-1] != nil
}
end
end
end
describe "path_to" do
it "should work with really simple arrays" do
[:a, :e,].path_to(:a).must_equal [0]
end
it "should work with simple arrays" do
[:a, [:b, :c], :e,].path_to(:c).must_equal [1, 1]
end
it "should work with arrays" do
[:a, [:b, :c, {:d => :foo}], :e,].path_to(:foo).must_equal [1, 2, :d]
end
it "should work with simple hashes" do
{:d => :foo}.path_to(:foo).must_equal [:d]
end
it "should work with hashes" do
({:a => [3, "foo"], :b => 5, :c => 2,}.path_to(3).must_equal [:a, 0])
end
end
This is the answer that I came up with.
class Object
def path_to obj; end
end
class Array
def path_to obj
if i = index(obj) then return [i] end
a = nil
_, i = to_enum.with_index.find{|e, _| a = e.path_to(obj)}
a.unshift(i) if i
end
end
class Hash
def path_to obj
if value?(obj) then return [key(obj)] end
a = nil
kv = find{|_, e| a = e.path_to(obj)}
a.unshift(kv.first) if kv
end
end

When declaring a method, what do the various argument prefixes mean?

When declaring a method, what do the various prefixes for the arguments mean?
sh(*cmd, &block)
What does the * before cmd mean?
What does the & before block mean?
The asterisk * means to combine all of the remaining arguments into a single list named by the argument. The ampersand & means that if a block is given to the method call (i.e. block_given? would be true) then store it in a new Proc named by the argument (or pseudo-argument, I guess).
def foo(*a)
puts a.inspect
end
foo(:ok) # => [:ok]
foo(1, 2, 3) # => [1, 2, 3]
def bar(&b)
puts b.inspect
end
bar() # => nil
bar() {|x| x+1} # => #<Proc:0x0000000100352748>
Note that the & must appear last, if used, and the * could be next-to-last before it, or it must be last.
The * operator can also be used to "expand" arrays into argument lists at call time (as opposed to "combining" them in a definition), like so:
def gah(a, b, c)
puts "OK: a=#{a}, b=#{b}, c=#{c}"
end
gah(*[1, 2, 3]) # => "OK: a=1, b=2, c=3"
gah(1, *[2, 3]) # => "OK: a=1, b=2, c=3" # must be last arg.
Similarly, the & operator can be used to "expand" a Proc object as the given block when calling a function:
def zap
yield [1, 2, 3] if block_given?
end
zap() # => nil
zap(&Proc.new{|x|puts x.inspect}) # => [1, 2, 3]

Resources