Using Refinements Hierarchically - ruby

Refinements was an experimental addition to v2.0, then modified and made permanent in v2.1. It provides a way to avoid "monkey-patching" by providing "a way to extend a class locally".
I attempted to apply Refinements to this recent question which I will simplify thus:
a = [[1, "a"],
[2, "b"],
[3, "c"],
[4, "d"]]
b = [[1, "AA"],
[2, "B"],
[3, "C"],
[5, "D"]]
The element at offset i in a matches the element at offset i in b if:
a[i].first == b[i].first
and
a[i].last.downcase == b[i].last.downcase
In other words, the matching of the strings is independent of case.
The problem is to determine the number of elements of a that match the corresponding element of b. We see that the answer is two, the elements at offsets 1 and 2.
One way to do this is to monkey-patch String#==:
class String
alias :dbl_eql :==
def ==(other)
downcase.dbl_eql(other.downcase)
end
end
a.zip(b).count { |ae,be| ae.zip(be).all? { |aee,bee| aee==bee } }
#=> 2
or instead use Refinements:
module M
refine String do
alias :dbl_eql :==
def ==(other)
downcase.dbl_eql(other.downcase)
end
end
end
'a' == 'A'
#=> false (as expected)
a.zip(b).count { |ae,be| ae.zip(be).all? { |aee,bee| aee==bee } }
#=> 0 (as expected)
using M
'a' == 'A'
#=> true
a.zip(b).count { |ae,be| ae.zip(be).all? { |aee,bee| aee==bee } }
#=> 2
However, I would like to use Refinements like this:
using M
a.zip(b).count { |ae,be| ae == be }
#=> 0
but, as you see, that gives the wrong answer. That's because I'm invoking Array#== and the refinement does not apply within Array.
I could do this:
module N
refine Array do
def ==(other)
zip(other).all? do |ae,be|
case ae
when String
ae.downcase==be.downcase
else
ae==be
end
end
end
end
end
using N
a.zip(b).count { |ae,be| ae == be }
#=> 2
but that's not what I want. I want to do something like this:
module N
refine Array do
using M
end
end
using N
a.zip(b).count { |ae,be| ae == be }
#=> 0
but clearly that does not work.
My question: is there a way to refine String for use in Array, then refine Array for use in my method?

Wow, this was really interesting to play around with! Thanks for asking this question! I found a way that works!
module M
refine String do
alias :dbl_eql :==
def ==(other)
downcase.dbl_eql(other.downcase)
end
end
refine Array do
def ==(other)
zip(other).all? {|x, y| x == y}
end
end
end
a = [[1, "a"],
[2, "b"],
[3, "c"],
[4, "d"]]
b = [[1, "AA"],
[2, "B"],
[3, "C"],
[5, "D"]]
using M
a.zip(b).count { |ae,be| ae == be } # 2
Without redefining == in Array, the refinement won't apply. Interestingly, it also doesn't work if you do it in two separate modules; this doesn't work, for instance:
module M
refine String do
alias :dbl_eql :==
def ==(other)
downcase.dbl_eql(other.downcase)
end
end
end
using M
module N
refine Array do
def ==(other)
zip(other).all? {|x, y| x == y}
end
end
end
a = [[1, "a"],
[2, "b"],
[3, "c"],
[4, "d"]]
b = [[1, "AA"],
[2, "B"],
[3, "C"],
[5, "D"]]
using N
a.zip(b).count { |ae,be| ae == be } # 0
I'm not familiar enough with the implementation details of refine to be totally confident about why this behavior occurs. My guess is that the inside of a refine block is treated sort of as entering a different top-level scope, similarly to how refines defined outside of the current file only apply if the file they are defined in is parsed with require in the current file. This would also explain why nested refines don't work; the interior refine goes out of scope the moment it exits. This would also explain why monkey-patching Array as follows works:
class Array
using M
def ==(other)
zip(other).all? {|x, y| x == y}
end
end
This doesn't fall prey to the scoping issues that refine creates, so the refine on String stays in scope.

Related

Passing block to select through send method

my_array = [[1, 'foo_parent', nil], [2,'bar_is_son_of_foo', 1], [3, 'zok_is_son_of_bar', 2]]
def children_block
Proc.new do |c|
if c.is_a? Array
c[2] == self[0]
end
end
end
my_array.send(:select) &children_block
gives me ArgumentError: wrong number of arguments (0 for 1..4)
You can do something like this to pass a block to select via send.
my_array.send(:select, &children_block)
Here is a working sample:
my_array = [[1, 'foo_parent', nil], [2,'bar_is_son_of_foo', 1], [3, 'zok_is_son_of_bar', 2]]
def children_block
Proc.new do |c|
p c
end
end
p my_array.send(:select, &children_block)
#=> [1, "foo_parent", nil]
[2, "bar_is_son_of_foo", 1]
[3, "zok_is_son_of_bar", 2]
The problem you are trying to solve can be also solved using:
my_array.select {|i| my_array.find {|j| j.first == i.last} }
or
my_array.select {|i| my_array.find {|j| i.first == j.last} }
depending on which elements you are interested in.
PS: There was another question today with similar issue with respect to passing block. First part of my answer is derivation of that question's answer
Blocks and lambdas/procs aren't the same, you can't just replace one with the other. The following should guide you in the right direction
my_array.send(:select) { |element| filter_somehow(element) }

Ruby, how to control the return value of calling an instanciated object

If I do this:
class PseudoRelationship
def my_method(args)
args
end
end
a = PseudoRelationship.new
I got as output
x
#<PseudoRelationship:0x109c2ebb0>
I would like it to behave like a Enumerator or Array, so I get for example this output
x = PseudoRelationship.new [1,2,3]
x
[1,2,3]
Pd. This is not for rails.
What I'm trying to do is behave like an array.
Its something that rails 2.3 seems to use, for example, you can do
my_model.relationship # returns an array
my_model.relationship.find #is a method
I'm trying to replicate that behaviour.
jholtrop was close, you want to overwrite the inspect method
2.0.0p645>
class PseudoRelationship
def initialize(args)
#args = args
end
def inspect
#args.inspect
end
end
2.0.0p645> PseudoRelationship.new [2, 3, 5]
[2, 3, 5]
--Edit based on OP's reasons for wanting this behavior--
While the above class displays what we want to see in the console, it doesn't actually assume any management of args in a way that treats args as an Enumerable. The OP's inspiration is drawn from a Rails construct, the ActiveRecord::Relation*. To imulate that style of behavior, you have to include Enumerable.
class PseudoRelationship
include Enumerable
def initialize(args)
#args = args
end
def each(&block)
#args.each(&block)
end
def inspect
#args.inspect
end
# Add extra functions to operate on #args
# This is obviously a silly example
def foo?
#args.include? :foo
end
def [](key)
#args[key]
end
def last
#args[-1]
end
end
2.0.0p645> PseudoRelationship.new [2, 3, 5]
[2, 3, 5]
2.0.0p645> x = PseudoRelationship.new [2, 3, 5]
[2, 3, 5]
2.0.0p645> x.each
#<Enumerator: [2, 3, 5]:each>
2.0.0p645> x.each_with_index
#<Enumerator: [2, 3, 5]:each_with_index>
2.0.0p645> x.each_with_index { |e, i| puts "#{i} => #{e}" }
0 => 2
1 => 3
2 => 5
[2, 3, 5]
2.0.0p645> x.foo?
false
2.0.0p645> x.first
2
2.0.0p645> x.last
5
2.0.0p645> x[1]
3
2.0.0p645> x[5]
nil
2.0.0p645> x
[2, 3, 5]
* This construct wasn't explictly stated, but I'm assuming based on the context
It sounds like you are asking to alter the "representation" of the object, not the "return value." To give the object a new String representation, you can define the #to_s method on the class. For example:
class PseudoRelationship
def initialize(v)
#v = v
end
def to_s
#v.to_s
end
end
Then in irb, for example:
1.9.3-p551 :021 > x = PseudoRelationship.new [:a, 42, false]
=> [:a, 42, false]
If you want the variable x to equal the arguments passed when instantiating the object, you need to add an initialize method to your class. Something like this should work:
class PseudoRelationship
def initialize(args)
args
end
def my_method(args)
args
end
end
x = PseudoRelationship.new [1, 2, 3]
x == [1, 2, 3] #=> true
EDIT:
Yes, the above won't work, really. If you want it to behave like an array, you could inherit from Array, but that can lead to strange issues and is generally not recommended. However, the below would work:
class PseudoRelationship < Array
end
x = PseudoRelationship.new [1, 2, 3]
x.class #=> PseudoRelationship < Array
x == [1, 2, 3] #=> true

How to auto save state of long iteration in ruby

Say I have a program with a runtime in the order of weeks with a structure like this:
(1..1000).each do |number|
('a'..'z').each do |letter|
%w(alpha beta omega whatever foo bar).each do |word|
do_long_running_calculation(number,letter,word)
end
end
end
Since the machine running the program may have a sudden unexpected halt, I'd like to save the index that it was on for each array to a file, such that it can re-start from where it left off instead of starting from the beginning in case of a sudden program abort.
Ultimately if this doesn't yet exist as a library (or easy solution that has evaded me), I'm going to make it myself and post it as an answer, but I would like to avoid re-inventing the wheel
Here's a way to deal with your specific example, which could be generalized.
First, two helpers:
class Range
def size # overwrite
case first
when Fixnum
last - first + 1
when String
last.ord - first.ord + 1
end
end
def [](offset)
case first
when Fixnum
first + offset
when String
(first.ord + offset).chr
end
end
end
For example:
(1..10).size #=> 10
('a'..'z').size #=> 26
(1..10)[4] #=> 5
('a'..'z')[4] #=> "e"
For the example:
loops = [(1..1000), ('a'..'z'), %w(alpha beta omega whatever foo bar)]
loop_sizes = loops.map(&:size)
#=> [1000, 26, 6]
prod = 1
tot_nbr_loops, *prod_loop_sizes = (loop_sizes + [1]).reverse.
map { |n| prod = n*prod }.reverse
#=> [156000, 156, 6, 1]
tot_nbr_loops
#=> 156000
prod_loop_sizes
#=> [156, 6, 1]
Using these objects we can create a method that maps a sequence of integers into the triples that are to be enumerated:
def elements(loops, prod_loop_sizes, offset)
loops.zip(prod_loop_sizes).map do |loop, prod|
div, offset = offset.divmod(prod)
loop[div]
end
end
Let's try it:
elements(loops, prod_loop_sizes, 0) #=> [1, "a", "alpha"]
elements(loops, prod_loop_sizes, 5) #=> [1, "a", "bar"]
elements(loops, prod_loop_sizes, 6) #=> [1, "b", "alpha"]
elements(loops, prod_loop_sizes, 155) #=> [1, "z", "bar"]
elements(loops, prod_loop_sizes, 156) #=> [2, "a", "alpha"]
elements(loops, prod_loop_sizes, 156) #=> [2, "a", "alpha"]
elements(loops, prod_loop_sizes, 156) #=> [2, "a", "alpha"]
elements(loops, prod_loop_sizes, 155999) #=> [1000, "z", "bar"]
So now you could write something like this:
save_interval = 10_000
total_number_loops.times do |i|
a,b,c = elements(loops, prod_loop_sizes, i)
# perform calculations with a,b,c
if i % save_interval == 0
<save the value of i and the current state>
<delete the previous saved state>
end
end
One easy way to save to (retrieve from) file most Ruby objects is to use the method Marshal#dump (Marshal#load). (Note the Marshal file format is not guaranteed to remain the same from one Ruby version to the next.)
Ruby provide us the trap method to handle the OS generated SIGNALS. When the system shutdown, then it send the SIGTERM signal. So all we need to handle that signal.
trap("TERM") do
write_data_to_file
end
def write_data_to_file
# code to save data in the file,
end
To read/write from file, cool answer is here:
https://stackoverflow.com/a/4310299/4136098

Stable #sort taking a block

We find here an implementation of a stable sort_by in Ruby, which works for the general case (i.e. I can supply my own comparision algorithm), and in this thread user tokland describes a very elegant way to do a stable sort_by:
module Enumerable
def stable_sort_by
sort_by.with_index { |x, idx| [yield(x), idx] }
end
end
The idea of using an Enumerator object together with with_index is surprisingly simple! I would like to find a similar elegant solution to create a stable version of the #sort function where it is given a comparison block. It would be used like this:
sorted_people = people.stable_sort do |person|
person.name
end
Here's a solution (but far from elegant):
module Enumerable
def stable_sort
each_with_index.sort { |(x, i), (y, j)|
r = yield(x, y)
r == 0 ? i <=> j : r
}.map(&:first)
end
end
It generates an array of [element, index] pairs and sorts them by passing each two elements to the given block (just like sort does). If the block returns 0, it compares the indices, otherwise, it returns the block's result. Afterwards, the elements are extracted from the generated array.
Example:
arr = [[2, :baz], [1,:foo], [1, :bar]]
arr.sort { |x, y| x[0] <=> y[0] }
#=> [[1, :bar], [1, :foo], [2, :baz]]
arr.stable_sort { |x, y| x[0] <=> y[0] }
#=> [[1, :foo], [1, :bar], [2, :baz]]

How do you iterate through an amalgamation of two Enumerables efficiently?

Given
a = nil # or [1,2]
b = [1,2] # or nil
Can you iterate through the concatenation of a and b without allocating an intermediate or creating massive amount of boiler plate code?
# meaning do this much more efficiently
((a || []) + (b || [])).each do |thing|
# more lines here
puts thing
end
This is kind of ugly:
l = lambda{|thing| do_my_thing }
a.each{|thing| l.call(thing)} if a
b.each{|thing| l.call(thing)} if b
Well, if you're willing to create a container (which should be cheap even if the elements contained are large), you could:
[a,b].compact.each do |e|
e.each do
# stuff
end
end
You do have to create a container array, but since you don't have to copy the contents of the sub-arrays (and instead are just dealing with two array pointers), it should be very quick and not terrible on the GC.
An alternate solution might just be to create a method:
def multi_each(*args,&block)
args.each do |a|
a.each(&block) if a
end
end
multi_each(nil,[1],[2,3]) do |i|
puts i
end
# 1
# 2
# 3
If you are using 1.9, I would use the ability for multiple splats:
a = nil
b = [1, 2]
[*a, *b]
#=> [1, 2]
a = [3, 4]
b = nil
[*a, *b]
#=> [3, 4]
So [*a, *b].each {} seems exactly like what you want.
What you have can be made far more concise and considerably less ugly by passing the lambda as the block itself:
l = lambda { |thing| do_my_thing }
a.each(&l) if a
b.each(&l) if b

Resources