Use variable's value in Proc definition - ruby

How can I use a variable's value at the point of Proc definition instead of defining the Proc with a reference to the variable? Or how else would I approach the problem of defining a list of different steps to be executed in sequence based on an input sequence?
Example:
arr = []
results = [1,2,3]
for res in results
arr << Proc.new { |_| res }
end
p arr[0].call(42)
p arr[1].call(3.14)
Expected output:
1
2
Actual output:
3
3

Why Your Code Doesn't Work as Expected: Shared Closure Scope
By definition, a Proc is a closure that retains its original scope but defers execution until called. Your non-idiomatic code obscures several subtle bugs, including the fact that the for-in control expression doesn't create a scope gate that provides the right context for your closures. All three of your Proc objects share the same scope, where the final assignment to the res variable is 3. As a result of their shared scope, you are correctly getting the same return value when calling any of the Procs stored in your array.
Fixing Your Closures
You can make your code work with some minor changes. For example:
arr = []
results = [1,2,3]
results.map do |res|
arr << Proc.new { |_| res }
end
p arr[0].call(42) #=> 1
p arr[1].call(3.14) #=> 2
Potential Refactorings
A More Idiomatic Approach
In addition to creating a proper scope gate, a more idiomatic refactoring might look like this:
results = [1, 2, 3]
arr = []
results.map { |i| arr << proc { i } }
arr.map { |proc_obj| proc_obj.call }
#=> [1, 2, 3]
Additional Refinements
A further refactoring could simplify the example code even further, especially if you don't need to store your inputs in an intermediate or explanatory variable like results. Consider:
array = [1, 2, 3].map { |i| proc { i } }
array.map &:call
#=> [1, 2, 3]
Validating the Refactoring
Because a Proc doesn't care about arity, this general approach also works when Proc#call is passed arbitrary arguments:
[42, 3.14, "a", nil].map { |v| arr[0].call(v) }
#=> [1, 1, 1, 1]
[42, 3.14, "a", nil].map { |v| arr[1].call(v) }
#=> [2, 2, 2, 2]

The problem is that the proc object use the context inside the loop the following should work
def proc_from_collection(collection)
procs = []
collection.each { |item| procs << Proc.new { |_| item } }
procs
end
results = [1,2,3]
arr = proc_from_collection(results)
p arr[0].call # -> 1
p arr[1].call # -> 2
After reading Todd A. Jacobs answer I felt like I was missing something.
Reading some post on stackoverflow about the for loop in ruby made me realize that we do not need a method here.
We can iterate the array using a method that does not pollute the global environment with unnecessary variables like the for loop does.
I suggest using a method whenever you need a proper closure that behaves according to a Lexical Scope (The body of a function is evaluated in the environment where the function is defined, not the environment
where the function is called.).
My first answer is still a good first approach but as pointed by Todd A. Jacobs a 'better' way to iterate the array could be enough in this case
arr = []
results = [1,2,3]
results.each { |item| arr << Proc.new { |_| item } }
p arr[0].call # -> 1
p arr[1].call # -> 2

Related

Way to refer to the receiver of 'Array#each'

I am iterating over an array, and I'm wondering if there's a shorthand to refer to the receiver of #each (or #each_with_index) method from within the iteration.
self returns main.
You should be able to just reference it:
my_thing.each {|one_thing| puts my_thing }
This is pretty similar to the answer I gave here https://stackoverflow.com/a/45421168/2981429 but slightly different.
First off, you can create a scope with self bound to the array, and then execute the each in that scope:
[1].instance_exec do
# in this scope, self is the array
# thus we can use just 'each' because the self is inferred
each do |x|
# note that since 'class' is a special keyword,
# it needs to be explicitly namespaced on self
puts self.class, x
end
end
# => prints Array, 1
You can create a utility function to do this, if you want:
def bound_each(enumerable, &blk)
enumerable.instance_exec { each &blk }
end
bound_each([1]) { |x| puts self.class, x }
# prints Array, 1
You can call your each method within an Object#tap block and reference the original receiver like that.
[1, 2, 3].tap { |i| i.each { |j| p i.dup << j } }
# [1, 2, 3, 1]
# [1, 2, 3, 2]
# [1, 2, 3, 3]
#=> [1, 2, 3]
Here the receiving object is [1, 2, 3] and is passed to the block-variable i which we can use locally or in nested scopes such as each's block.
Avoid modifying the receiving object else you may end up with undesired results such as an infinite array. Using dup could allay this possibility.
This is an interesting question. As far as I know it's not possible – the closest I can come up with would be to use inject (or reduce) and explicitly pass the receiver as an argument. A bit pointless, but there might be a use-case for it that I'm not seeing:
a = [1,2,3]
a.inject(a) do |this, element|
this == a #=> true
this.include?(element) #=> true
this
end
Apart from looking a bit redundant, you have to be very sure to return this at the end of each iteration, as the return value will become this in the next iteration. For that reason (and the fact that you could just reference your collection in an each block, as in David's answer) I don't recommend using this.
Edit - as Simple Lime pointed out in the comments – I missed the obvious Enumerator#with_object, which has the same (rather pointless) effect, but without the drawback of having to return this at the end of each iteration. For example:
a = [1,2,3]
a.map.with_object(a) do |element, this|
this == a #=> true, for each iteration
end
I still don't recommend that you use this though.

Guidance on how to implement certain problems in Ruby

For the class NumberSet, I have to define the [] method so that it takes one argument, which it uses as a filter to select members of the set and returns a new set containing those elements.
For example, I have
Filter.new { |number| number.even? }
and
SignFilter.new(:non_negative)
which are classes that I must construct.
I also have to define the & and | operators so that they work with the filters. Something along the lines of
numbers[SignFilter.new(:non_negative) & Filter.new { |number| number.even? }]
The class so far is:
class NumberSet
include Enumerable
def initialize
#arr=[]
end
def each (&block)
#arr.each do |member|
block.call (member)
end
end
def << number
#arr<<number unless #arr.include?(number)
end
end
For Filter I think something like works:
class Filter
def initialize
yield
end
end
My biggest problem is the [], $and | parts, which I have no idea how to do.
What I want to accomplish is :
numbers = NumberSet.new
[-3, -2, -1, 0, 1, 2, 3, 4, 5].each do |number|
numbers << number
end
numbers[SignFilter.new(:non_negative) & Filter.new { |number| number.even?}].to_a
#=>[0, 2, 4]
I think this should do it.
Code
SignFilter class
class SignFilter
def initialize(sign)
#sign = sign
end
def filter
case #sign
when :negative
-> i { i < 0 }
when :positive
-> i { i > 0 }
when :non_negative
-> i { i >= 0 }
when :non_positive
-> i { i <= 0 }
when :zero
-> i { i.zero? }
end
end
end
Filter class
class Filter
attr_reader :filter
def initialize &filter
#filter = filter
end
end
NumberSet class
class NumberSet
def initialize arr
#arr = arr
end
def process(op, sf, f)
sign_filter = sf.filter
filter = f.filter
#arr.send(op).each { |e| sign_filter.call(e) }
.send(op).each { |e| filter.call(e) }
end
end
Examples
sf = SignFilter.new :negative
f = Filter.new { |number| number.even? }
ns = NumberSet.new [-3, -2, -1, 0, 1, 2, 3, 4, 5]
ns.process(:reject, sf, f)
#=> [1, 3, 5]
sf = SignFilter.new :non_negative
f = Filter.new { |number| number % 3 == 0 }
ns = NumberSet.new [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6]
ns.process(:select, sf, f)
#=> [0, 3, 6]
Explanation
Blocks are not objects, so you can't pass them around. They can be converted to procs, however, which are objects (instances of the class Proc), and therefore can be passed. You will need to understand procs (and lambdas, which are a type of proc). Here is a good explanation. (Aside: one thing that's a little confusing about procs is the multitude of ways they can be invoked. For example, if p -> i { 3*i }, p.call(2), p.yield(2), p[2] and p.(2) all return the value 6.)
Here is what is happening:
An instance sf of the SignFilter class is created by passing the symbol denoting the signs of the elements of the array that are to be selected or rejected (:negative, :non_negative and so on). The symbol is saved in an instance variable #sign.
The SignFilter class has a method filter which returns a lambda based on the value of #sign.
An instance of the Filter class is created by passing a block which is received as a proc and saved into the instance variable #filter, which has a read accessor.
An instance of the NumberSet class is created by passing the array, which is saved into the instance variable #arr.
NumberSet#process is passed three arguments: the Enumerable method :reject or :select (saved into op), the SignFilter instance (sf) and the Filter instance (f).
Within NumberSet#process, #arr is sent op, which creates an enumerator (arr.reject or arr.select).
The SignFilter lambda is called on each element of the enumerator. An array comprised of a subcollection of #arr is returned.
The returned array is sent op, creating another reject or select enumerator.
The Filter proc is called on the each element of the new enumerator, causing the desired array to be returned.
Considering that both reject and select are not necessary (either would do), it would have been simpler to just replace .send(op) in NumberSet#process with, say, reject, but I chose to do it this way just to illustrate the generalization.
Notice that it would be easy to generalize NumberSet#process to process(sf,f,o), where, as now, sf and f provide filters and o would be an instance of an Operation class that specifies a proc to send to the filtered subcollection of #arr to invoke an Enumerable method with a block (map, group_by and so on).
Since this appears to be a homework question, I'm not going to give you the full implementation, but I'll see you on your way. The big thing you need to realize here is that methods such as [] and | can be defined as normal methods in Ruby. In Ruby, you simply send messages (such as []) to a "receiver". How you handle those methods are defined via def instructions on your class definition. So, for example:
class Filter
def |(other_filter)
# Do some work to build a new filter which is a union of this filter and other_filter
end
end
class SignFilter < Filter
# SignFilter inherits the & method from Filter, and you can then go on to implement SignFilter-specific functionality
# ...
end
class NumberSet
def [](filter)
# Given a filter, apply each of the numbers of this NumberSet to the filter, and return those which pass the filter
end
end
To invoke those methods, would do something like:
filter = Filter.new {|num| num.even? }
sign_filter = SignFilter.new(:non_negative)
union_filter = filter | sign_filter
This third line is equivalent to calling:
union_filter = filter.|(sign_filter)
or:
union_filter = filter.send("|", sign_filter)
In all three cases, the "|" message is sent to the the filter instance, with sign_filter as an additional argument. You could also do something like:
numset = NumberSet.new(1,2,3,4,5)
filtered_numbers = numset[union_filter]
This second line is equivalent to:
numset.[](union_filter)
# or
numset.send("[]", union_filter)
The original syntax is just syntactic sugar for those method calls - it's just there because it makes Ruby code look nicer, and helps the programmer mentally map it to common conventions for those kinds of operations.

ruby enumerables: is there a detect for results of block evaluation?

I'm looking for something similar to #detect in enumerables, but not quite. This is what enumerable does:
[1, 2, 3].detect {|i| i > 1 } #=> 2
it returns the first instance of the array which matches the condition. Now, my purpose is to return the value of the block. Concern is not exactly the conditions, but for instance, the first which is not nil. Something like this:
[var1, var2, var3].wanted_detect {|var| another_function(var) }
in which the function would return the first result of another_function call which isn't nil.
Mapping the values of applying the method on the variables and then using detect is not an option. This one would ideally have to work in lazy enumerators, for which the early mapping of all possible values is a no-go
[var1, var2, var3].lazy.map { |var| another_function(var) }.reject(&:nil?).first
If you don't have access to Enumerable#lazy, it is easy enough to implement what you want:
module Enumerable
def wanted_detect
self.each do |obj|
val = yield obj
return val if val
end
end
end
Demo:
[1, 2, 3, 4].wanted_detect { |x| x*x if x > 2 }
# => 9
EDIT: Sorry, I missed the last paragraph till falsetru pointed it out.
Thanks for the comments, falsetru.

Does Hash override Enumerable#map()?

Given that map() is defined by Enumerable, how can Hash#map yield two variables to its block? Does Hash override Enumerable#map()?
Here's a little example, for fun:
ruby-1.9.2-p180 :001 > {"herp" => "derp"}.map{|k,v| k+v}
=> ["herpderp"]
It doesn't override map
Hash.new.method(:map).owner # => Enumerable
It yields two variables which get collected into an array
class Nums
include Enumerable
def each
yield 1
yield 1, 2
yield 3, 4, 5
end
end
Nums.new.to_a # => [1, [1, 2], [3, 4, 5]]
Given that map() is defined by Enumerable, how can Hash#map yield two variables to its block?
It doesn't. It yields a single object to its block, which is a two-element array consisting of the key and the value.
It's just destructuring bind:
def without_destructuring(a, b) end
without_destructuring([1, 2])
# ArgumentError: wrong number of arguments (1 for 2)
def with_destructuring((a, b)) end # Note the extra parentheses
with_destructuring([1, 2])
def with_nested_destructuring((a, (b, c))) p a; p b; p c end
with_nested_destructuring([1, [2, 3]])
# 1
# 2
# 3
# Note the similarity to
a, (b, c) = [1, [2, 3]]
Theoretically, you would have to call map like this:
hsh.map {|(k, v)| ... }
And, in fact, for inject, you actually need to do that:
hsh.inject {|acc, (k, v)| ... }
However, Ruby is more lenient with argument checking for blocks than it is for methods. In particular:
If you yield more than one object, but the block only takes a single argument, all the objects are collected into an array.
If you yield a single object, but the block takes multiple arguments, Ruby performs destructuring bind. (This is the case here.)
If you yield more objects than the block takes arguments, the extra objects get ignored.
If you the block takes more arguments than you are yielding, the extra arguments are bound to nil.
Basically, the same semantics as parallel assignment.
In fact, before Ruby 1.9, block arguments actually did have assignment semantics. This allowed you to do crazy things like this:
class << (a = Object.new); attr_accessor :b end
def wtf; yield 1, 2 end
wtf {|#a, a.b| } # WTF? The block body is empty!
p #a
# 1
p a.b
# 2
This crazy stuff works (in 1.8 and older), because block argument passing is treated the same as assignment. IOW, even though the above block is empty and doesn't do anything, the fact that block arguments are passed as if they had been assigned, means that #a is set and the a.b= setter method is called. Crazy, huh? That's why it was removed in 1.9.
If you want to startle your co-workers, stop defining your setters like this:
attr_writer :foo
and instead define them like this:
define_method(:foo=) {|#foo|}
Just make sure someone else ends up maintaining it :-)

In Ruby, is there an Array method that combines 'select' and 'map'?

I have a Ruby array containing some string values. I need to:
Find all elements that match some predicate
Run the matching elements through a transformation
Return the results as an array
Right now my solution looks like this:
def example
matchingLines = #lines.select{ |line| ... }
results = matchingLines.map{ |line| ... }
return results.uniq.sort
end
Is there an Array or Enumerable method that combines select and map into a single logical statement?
I usually use map and compact together along with my selection criteria as a postfix if. compact gets rid of the nils.
jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1}
=> [3, 3, 3, nil, nil, nil]
jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1}.compact
=> [3, 3, 3]
Ruby 2.7+
There is now!
Ruby 2.7 is introducing filter_map for this exact purpose. It's idiomatic and performant, and I'd expect it to become the norm very soon.
For example:
numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]
Here's a good read on the subject.
Hope that's useful to someone!
You can use reduce for this, which requires only one pass:
[1,1,1,2,3,4].reduce([]) { |a, n| a.push(n*3) if n==1; a }
=> [3, 3, 3]
In other words, initialize the state to be what you want (in our case, an empty list to fill: []), then always make sure to return this value with modifications for each element in the original list (in our case, the modified element pushed to the list).
This is the most efficient since it only loops over the list with one pass (map + select or compact requires two passes).
In your case:
def example
results = #lines.reduce([]) do |lines, line|
lines.push( ...(line) ) if ...
lines
end
return results.uniq.sort
end
Another different way of approaching this is using the new (relative to this question) Enumerator::Lazy:
def example
#lines.lazy
.select { |line| line.property == requirement }
.map { |line| transforming_method(line) }
.uniq
.sort
end
The .lazy method returns a lazy enumerator. Calling .select or .map on a lazy enumerator returns another lazy enumerator. Only once you call .uniq does it actually force the enumerator and return an array. So what effectively happens is your .select and .map calls are combined into one - you only iterate over #lines once to do both .select and .map.
My instinct is that Adam's reduce method will be a little faster, but I think this is far more readable.
The primary consequence of this is that no intermediate array objects are created for each subsequent method call. In a normal #lines.select.map situation, select returns an array which is then modified by map, again returning an array. By comparison, the lazy evaluation only creates an array once. This is useful when your initial collection object is large. It also empowers you to work with infinite enumerators - e.g. random_number_generator.lazy.select(&:odd?).take(10).
If you have a select that can use the case operator (===), grep is a good alternative:
p [1,2,'not_a_number',3].grep(Integer){|x| -x } #=> [-1, -2, -3]
p ['1','2','not_a_number','3'].grep(/\D/, &:upcase) #=> ["NOT_A_NUMBER"]
If we need more complex logic we can create lambdas:
my_favourite_numbers = [1,4,6]
is_a_favourite_number = -> x { my_favourite_numbers.include? x }
make_awesome = -> x { "***#{x}***" }
my_data = [1,2,3,4]
p my_data.grep(is_a_favourite_number, &make_awesome) #=> ["***1***", "***4***"]
I'm not sure there is one. The Enumerable module, which adds select and map, doesn't show one.
You'd be required to pass in two blocks to the select_and_transform method, which would be a bit unintuitive IMHO.
Obviously, you could just chain them together, which is more readable:
transformed_list = lines.select{|line| ...}.map{|line| ... }
Simple Answer:
If you have n records, and you want to select and map based on condition then
records.map { |record| record.attribute if condition }.compact
Here, attribute is whatever you want from the record and condition you can put any check.
compact is to flush the unnecessary nil's which came out of that if condition
No, but you can do it like this:
lines.map { |line| do_some_action if check_some_property }.reject(&:nil?)
Or even better:
lines.inject([]) { |all, line| all << line if check_some_property; all }
I think that this way is more readable, because splits the filter conditions and mapped value while remaining clear that the actions are connected:
results = #lines.select { |line|
line.should_include?
}.map do |line|
line.value_to_map
end
And, in your specific case, eliminate the result variable all together:
def example
#lines.select { |line|
line.should_include?
}.map { |line|
line.value_to_map
}.uniq.sort
end
def example
#lines.select {|line| ... }.map {|line| ... }.uniq.sort
end
In Ruby 1.9 and 1.8.7, you can also chain and wrap iterators by simply not passing a block to them:
enum.select.map {|bla| ... }
But it's not really possible in this case, since the types of the block return values of select and map don't match up. It makes more sense for something like this:
enum.inject.with_index {|(acc, el), idx| ... }
AFAICS, the best you can do is the first example.
Here's a small example:
%w[a b 1 2 c d].map.select {|e| if /[0-9]/ =~ e then false else e.upcase end }
# => ["a", "b", "c", "d"]
%w[a b 1 2 c d].select.map {|e| if /[0-9]/ =~ e then false else e.upcase end }
# => ["A", "B", false, false, "C", "D"]
But what you really want is ["A", "B", "C", "D"].
You should try using my library Rearmed Ruby in which I have added the method Enumerable#select_map. Heres an example:
items = [{version: "1.1"}, {version: nil}, {version: false}]
items.select_map{|x| x[:version]} #=> [{version: "1.1"}]
# or without enumerable monkey patch
Rearmed.select_map(items){|x| x[:version]}
If you want to not create two different arrays, you can use compact! but be careful about it.
array = [1,1,1,2,3,4]
new_array = map{|n| n*3 if n==1}
new_array.compact!
Interestingly, compact! does an in place removal of nil. The return value of compact! is the same array if there were changes but nil if there were no nils.
array = [1,1,1,2,3,4]
new_array = map{|n| n*3 if n==1}.tap { |array| array.compact! }
Would be a one liner.
Your version:
def example
matchingLines = #lines.select{ |line| ... }
results = matchingLines.map{ |line| ... }
return results.uniq.sort
end
My version:
def example
results = {}
#lines.each{ |line| results[line] = true if ... }
return results.keys.sort
end
This will do 1 iteration (except the sort), and has the added bonus of keeping uniqueness (if you don't care about uniq, then just make results an array and results.push(line) if ...
Here is a example. It is not the same as your problem, but may be what you want, or can give a clue to your solution:
def example
lines.each do |x|
new_value = do_transform(x)
if new_value == some_thing
return new_value # here jump out example method directly.
else
next # continue next iterate.
end
end
end

Resources