Symbol#to_proc with parameters [duplicate] - ruby

This question already has answers here:
Can you supply arguments to the map(&:method) syntax in Ruby?
(9 answers)
Closed 8 years ago.
Given the following array a:
a = [1, 2, 3, 4, 5]
How do I do:
a.map { |num| num + 1 }
using the short notation:
a.map(&:+ 1)
or:
a.map(&:+ 2)
where 1 and 2 are the arguments?

In this case you can do
a.map(&1.method(:+))
But only because 1 + x is usually the same as x + 1.
Here is a discussion of this practice in a performance context.

You can't do it like this. The & operator is for turning symbols into procs.
a = [1, 2, 3, 4, 5]
puts a.map(&:to_s) # prints array of strings
puts a.map(&:to_s2) # error, no such method `to_s2`.
& is a shorthand for to_proc:
def to_proc
proc { |obj, *args| obj.send(self, *args) }
end
It creates and returns new proc. As you see, you can't pass any parameters to this method. You can only call the generated proc.

You cannot do it with map. But look at Facets' Enumerable#map_send:
require 'facets'
[1, 2, 3].map_send(:+, 1)
#=> [2, 3, 4]
Writing your own implementation is pretty straightforward:
module Enumerable
def map_send(*args)
map { |obj| obj.send(*args) }
end
end

If you really need that you can use Ampex library, but I don't know if it is still maintained.

Related

Reassign entire array to the same reference

I've searched extensively but sadly couldn't find a solution to this surely often-asked question.
In Perl I can reassign an entire array within a function and have my changes reflected outside the function:
#!/usr/bin/perl -w
use v5.20;
use Data::Dumper;
sub foo {
my ($ref) = #_;
#$ref = (3, 4, 5);
}
my $ref = [1, 2];
foo($ref);
say Dumper $ref; # prints [3, 4, 5]
Now I'm trying to learn Ruby and have written a function where I'd like to change an array items in-place by filtering out elements matching a condition and returning the removed items:
def filterItems(items)
removed, items = items.partition { ... }
After running the function, items returns to its state before calling the function. How should I approach this please?
I'd like to change an array items in-place by filtering out elements matching a condition and returning the removed items [...] How should I approach this please?
You could replace the array content within your method:
def filter_items(items)
removed, kept = items.partition { |i| i.odd? }
items.replace(kept)
removed
end
ary = [1, 2, 3, 4, 5]
filter_items(ary)
#=> [1, 3, 5]
ary
#=> [2, 4]
I would search for pass by value/reference in ruby. Here is one I found first https://mixandgo.com/learn/is-ruby-pass-by-reference-or-pass-by-value.
You pass reference value of items to the function, not the reference to items. Variable items is defined out of method scope and always refers to same value, unless you reassign it in the variable scope.
Also filterItems is not ruby style, see https://rubystyle.guide/
TL;DR
To access or modify an outer variable within a block, declare the variable outside the block. To access a variable outside of a method, store it in an instance or class variable. There's a lot more to it than that, but this covers the use case in your original post.
Explanation and Examples
In Ruby, you have scope gates and closures. In particular, methods and blocks represent scope gates, but there are certainly ways (both routine and meta) for accessing variables outside of your local scope.
In a class, this is usually handled by instance variables. So, as a simple example of String#parition (because it's easier to explain than Enumerable#partition on an Array):
def filter items, separator
head, sep, tail = items.partition separator
#items = tail
end
filter "foobarbaz", "bar"
#=> "baz"
#items
#=> "baz"
Inside a class or within irb, this will modify whatever's passed and then assign it to the instance variable outside the method.
Partitioning Arrays Instead of Strings
If you really don't want to pass things as arguments, or if #items should be an Array, then you can certainly do that too. However, Arrays behave differently, so I'm not sure what you really expect Array#partition (which is inherited from Enumerable) to yield. This works, using Enumerable#slice_after:
class Filter
def initialize
#items = []
end
def filter_array items, separator
#items = [3,4,5].slice_after { |i| i == separator }.to_a.pop
end
end
f = Filter.new
f.filter_array [3, 4, 5], 4
#=> [5]
Look into the Array class for any method which mutates the object, for example all the method with a bang or methods that insert elements.
Here is an Array#push:
ary = [1,2,3,4,5]
def foo(ary)
ary.push *[6, 7]
end
foo(ary)
ary
#=> [1, 2, 3, 4, 5, 6, 7]
Here is an Array#insert:
ary = [1,2,3,4,5]
def baz(ary)
ary.insert(2, 10, 20)
end
baz(ary)
ary
#=> [1, 2, 10, 20, 3, 4, 5]
Here is an example with a bang Array#reject!:
ary = [1,2,3,4,5]
def zoo(ary)
ary.reject!(&:even?)
end
zoo(ary)
ary
#=> [1, 3, 5]
Another with a bang Array#map!:
ary = [1,2,3,4,5]
def bar(ary)
ary.map! { |e| e**2 }
end
bar(ary)
ary
#=> [1, 4, 9, 16, 25]

Is there a default block argument in Ruby?

I am just starting to do Groovy after mostly doing ruby.
It has a default 'block argument', it, as it were, not officially the terminology for Groovy, but I'm new to Groovy.
(1..10).each {println(it)}
What about Ruby? Is there a default I can use so I don't have to make |my_block_arg| every time?
Thanks!
No, you don't have a "default" in Ruby.
Though, you can do
(1..10).each(&method(:puts))
Like Andrey Deinekos answer explained there is no default. You can set the self context using BasicObject#instance_eval or BasicObject#instance_exec. I don't recommend doing this since it can sometimes result in some unexpected results. However if you know what you're doing the following is still an option:
class Enumerator
def with_ie(&block)
return to_enum(__method__) { each.size } unless block_given?
each { |e| e.instance_eval(&block) }
end
end
(1..10).each.with_ie { puts self }
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
# 10
#=> 1..10
(1..10).map.with_ie { self * self }
#=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
(-5..5).select.with_ie { positive? }
#=> [1, 2, 3, 4, 5]
If you want to call one method you might as well do (-5..5).select(&:positive?), but when the objects you're iterating over have actual attributes it might be worth the trouble. For example:
people.map.with_ie { "#{id}: #{first_name} - #{last_name}" }
Keep in mind that if you have an local variable id, first_name or last_name in scope those are used instead of the methods on the object. This also doesn't quite work for hashes or Enumerable methods that pass more than one block argument. In this case self is set to an array containing the arguments. For example:
{a: 1, b: 2}.map.with_ie { self }
#=> [[:a, 1], [:b, 2]]
{a: 1, b: 2}.map.with_ie { self[0] }
#=> [:a, :b]
From Ruby 2.7 onwards, you can use numbered block arguments:
(1..10).each { puts _1 }
Granted, this hasn't been very well documented; some references are still using #1, but the above is tested on the official 2.7 version.

How to get reference to object you're calling in method?

I am trying to create a method for objects that I create. In this case it's an extension of the Array class. The method below, my_uniq, works. When I call puts [1, 1, 2, 6, 8, 8, 9].my_uniq.to_s it outputs to [1, 2, 6, 8].
In a similar manner, in median, I'm trying to get the reference of the object itself and then manipulate that data. So far I can only think of using the map function to assign a variable arr as an array to manipulate that data from.
Is there any method that you can call that gets the reference to what you're trying to manipulate? Example pseudo-code that I could replace arr = map {|n| n} with something like: arr = self.
class Array
def my_uniq
hash = {}
each do |num|
hash[num] = 0;
end
hash.keys
end
end
class Array
def median
arr = map {|n| n}
puts arr.to_s
end
end
Thanks in advance!
dup
class Array
def new_self
dup
end
def plus_one
arr = dup
arr.map! { |i| i + 1 }
end
def plus_one!
arr = self
arr.map! { |i| i + 1 }
end
end
array = [1, 3, 5]
array.new_self # => [1, 3, 5]
array.plus_one # => [2, 4, 6]
array # => [1, 3, 5]
array.plus_one! # => [2, 4, 6]
array # => [2, 4, 6]
dup makes a copy of the object, making it a safer choice if you need to manipulate data without mutating the original object. You could use self i.e. arr = self, but anything you do that changes arr will also change the value of self. It's a good idea to just use dup.
If you do want to manipulate and change the original object, then you can use self instead of dup, but you should make it a "bang" ! method. It is a convention in ruby to put a bang ! at the end of a method name if it mutates the receiving object. This is particularly important if other developers might use your code. Most Ruby developers would be very surprised if a non-bang method mutated the receiving object.
class Array
def median
arr = self
puts arr.to_s
end
end
[1,2,3].median # => [1,2,3]

Ruby print function [duplicate]

This question already has answers here:
Why am I getting objects printed twice?
(4 answers)
Closed 6 years ago.
# Call the each method of each collection in turn.
# This is not a parallel iteration and does not require enumerators.
def sequence(*enumerables, &block)
enumerables.each do |enumerable|
enumerable.each(&block)
end
end
# Examples of how these iterator methods work
a,b,c = [1,2,3],4..6,'a'..'e'
print "#{sequence(a,b,c) {|x| print x}}\n"
why the results is:
123456abcde[[1, 2, 3], 4..6, "a".."e"]
anyone could tell me why [[1, 2, 3], 4..6, "a".."e"] is getting printed?
or tell me why the return value of the 'sequence' method is [[1, 2, 3], 4..6, "a".."e"]??
Many thanks
sequence(a,b,c) { |x| print x }
prints 123456abcde and
print "#{some_code}\n"
will print the return value of some_code. In your example the each loops returns [[1, 2, 3], 4..6, "a".."e"], because the return value if each is self (see: http://apidock.com/ruby/v1_9_3_392/Enumerator/each)

How do I pass an argument to array.map short cut? [duplicate]

This question already has answers here:
Can you supply arguments to the map(&:method) syntax in Ruby?
(9 answers)
Closed 8 years ago.
Given the following array a:
a = [1, 2, 3, 4, 5]
How do I do:
a.map { |num| num + 1 }
using the short notation:
a.map(&:+ 1)
or:
a.map(&:+ 2)
where 1 and 2 are the arguments?
In this case you can do
a.map(&1.method(:+))
But only because 1 + x is usually the same as x + 1.
Here is a discussion of this practice in a performance context.
You can't do it like this. The & operator is for turning symbols into procs.
a = [1, 2, 3, 4, 5]
puts a.map(&:to_s) # prints array of strings
puts a.map(&:to_s2) # error, no such method `to_s2`.
& is a shorthand for to_proc:
def to_proc
proc { |obj, *args| obj.send(self, *args) }
end
It creates and returns new proc. As you see, you can't pass any parameters to this method. You can only call the generated proc.
You cannot do it with map. But look at Facets' Enumerable#map_send:
require 'facets'
[1, 2, 3].map_send(:+, 1)
#=> [2, 3, 4]
Writing your own implementation is pretty straightforward:
module Enumerable
def map_send(*args)
map { |obj| obj.send(*args) }
end
end
If you really need that you can use Ampex library, but I don't know if it is still maintained.

Resources