Refactoring out a for loop ruby - ruby

I am having problems refactoring out some duplicated code from two methods sharing a for loop. The two methods with the duplicated code are gcdOfFiveUpToFive and remainderStepsUpToFive. The two loops share in common setting instance variable #m to 5 and the both use a for x in 1..5 loop and then set #n to x as well as both of them need to call euclidGCD although one calls euclidGCD for its return value and the other to add +=1 to the #count variable. I do want want to return 2 values from one method. I guess I could make a 4th instance variable called #countArray and get an array of the remainder step count.
require 'minitest/autorun'
class GCDTest < Minitest::Test
def test_euclid_gcd
gcdTestObject=GCD.new(20,5)
assert gcdTestObject.euclidGcd==5
assert gcdTestObject.gcdRemainderSteps==1
end
def test_euclid_two
gcdTestObject=GCD.new(13,8)
assert gcdTestObject.euclidGcd==1
assert gcdTestObject.gcdRemainderSteps==5
end
def test_euclid_loop
gcdTestObject=GCD.new(0,0)
assert gcdTestObject.gcdOfFiveUpToFive==[1,1,1,1,5]
end
def test_count_of_loop
gcdTestObject=GCD.new(0,0)
assert gcdTestObject.remainderStepsUpToFive==[1,2,3,2,1]
end
end
class GCD
attr_accessor :m,:n
attr_reader :count
def initialize(m,n)
#m=m
#n=n
#count=0
end
def euclidGcd
#count=1
m=#m
n=#n
r= m % n
until r==0
m=n
n=r
r= m % n
#count+=1
end
return n
end
def gcdRemainderSteps
return #count
end
def gcdOfFiveUpToFive
#m=5
gcdArrayUpToFive=[]
for x in 1..5
#n=x
gcdArrayUpToFive << euclidGcd
end
return gcdArrayUpToFive
end
def remainderStepsUpToFive
#m=5
gcdStepArrayUpToFive=[]
for x in 1..5
#n=x
euclidGcd
gcdStepArrayUpToFive << gcdRemainderSteps
end
return gcdStepArrayUpToFive
end
def fiveLoopExtraction
end

Code that repeats itself is this:
array=[]
for x in 1..5
# result = do something with x
array << result
end
return array
That is exactly what map function does.
What does the "map" method do in Ruby?
Ruby methods names should be snake_case. Lets refactor this to use proper naming convention and map function.
def gcd_of_five_up_to_five
#m=5
(1..5).map do |x|
#n = x
# in ruby you don't have to write return
# value of last expression is returned automatically
euclid_gcd
end
end
def remainder_steps_up_to_five
#m=5
(1..5).map do |x|
#n = x
euclid_gcd
gcd_remainder_steps
end
end
I'd call it with params instead of using #m and #n. That would simplify the code. If you change euclid_gcd to this: def euclid_gcd(m:, n:) you'd get this:
def gcd_of_5_up_to_5
(1..5).map { |x| euclid_gcd(m: 5, n: x) }
end
def remainder_steps_up_to_5
(1..5).map do |x|
euclid_gcd(m: 5, n: x)
gcd_remainder_steps
end
end
Seems like this needs little or no further refactoring.

Related

Ruby - how to raise same error for multiple methods without writing it multiple times?

Lets say I create a Calculator class that works by manipulating elements in an array - within this class I define several methods: add, subtract, multiply, divide. I want each method to raise the same error if there exist only 1 or fewer elements in the array, something like:
class Calculator
# ...
def add
if #array.length < 2
raise 'Not Enough Elements'
else
#array << #array.pop + #array.pop
end
end
# ...
end
I could write a condition to raise the error into each method, but that seems very tedious and un-Ruby. Would there be a way to apply the raised error to all the methods that would need it that would save all that typing?
One of the options would be moving the length checking logic into it's own method and using it where needed:
class Calculator
def add
check_array_length
# rest of the method
end
private
def check_array_length
raise 'Not Enough Elements' if #array.length < 2
end
end
If you are setting #array in the initialize method, you could raise on the early stage, saying that you can't proceed because of too less elements in the #array:
class Calculator
def initialize(array)
raise 'Not Enough Elements' if array.length < 2
#array = array
end
end
Here's a possible structure :
module Validator
[:add, :substract, :multiply, :divide].each do |method|
define_method(method) do
validate_array_length(2)
super()
end
end
private
def validate_array_length(min,max=min)
raise 'Not Enough Elements' if #array.length < min
raise 'Too Many Elements' if #array.length > max
end
end
class Calculator
prepend Validator
def initialize(*values)
#array = values
end
def add
#array << #array.pop + #array.pop
end
# def substract ....
end
c = Calculator.new(3,2)
c.add
c.add
# => calculator.rb:12:in `validate_array_length': Not Enough Elements (RuntimeError)
class Calculator
def initialize(arr)
#arr = arr
end
def add; binary(:+); end
def subtract; binary(:-); end
def multiply; binary(:*); end
def divide; binary(:/); end
def power; binary(:**); end
def modulo; binary(:%); end
# ... (others)
def negate; unary(:-#); end
def odd?; unary(:odd?); end
def even?; unary(:even?); end
def to_f; unary(:to_f); end
# ... (others)
private
def binary(op)
raise ArgumentError, 'Too few elements' if #arr.length < 2
#arr.pop.send(op, #arr.pop)
end
def unary(op)
raise ArgumentError, 'Too few elements' if #arr.length.zero?
#arr.pop.send(op)
end
end
# add neg mod pow div mult sub add
calc = Calculator.new [ 1, 5, 2,3, 4,5, 6,7, 8,9, 10,11, 12,13]
#=> #<Calculator:0x007fa192030968 #arr=[1, 5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]>
calc.add #=> 25 (13+12)
calc.subtract #=> 1 (11-10)
calc.multiply #=> 72 (8*9)
calc.divide #=> 1 (7/6)
calc.power #=> 625 (5**4)
calc.modulo #=> 1 (3%2)
calc.negate #=> -5 (5)
calc.add #=> ArgumentError: Too few elements
It appears that you are constructing an RPN calculator. If so, you probably want to push the result of each calculation back onto the stack. For binary operators you could change the method binary as follows:
def binary(op)
raise ArgumentError, 'Too few elements' if #arr.length < 2
#arr << #arr.pop.send(op, #arr.pop)
#arr[-1]
end
#arr[-1], the result of the calculation, is the return value. The modification of unary is similar.
You may wish to add some stack manipulation methods, such as
def pop
#arr.pop
end
def push(n)
#arr << n
end
def swap
#arr[-1], #arr[-2] = #arr[-2], #arr[-1]
end
def rotate
#arr.rotate
end
Lastly, you might find it it clearer to make the beginning (rather than end) of #arr the top of the stack, in which you would use unshift/shift rather than push/pop.

Assigning value to self in Ruby

I have the following class in Ruby:
class TOMatrix < Matrix
def initialize
self = Matrix.build(8, 8){|r, c| 0}
end
But, this gives the error:
Cannot assign to a keyword
Does anyone know how to achive what a I need ?
It seems you need to write a wrapper instead of subclassing the Matrix (subclassing in your case might break Liskov's substitution principle):
require 'matrix'
class TOMatrix
def initialize
#matrix = Matrix.build(8, 8){|r, c| 0}
end
def method_missing(*args, &block)
#matrix.send(*args, &block)
end
def respond_to?(method)
super || #matrix.respond_to?(method)
end
end
m = TOMatrix.new
m.hermitian? #=> true
You cannot change the value of self directly. You probably want to do something like this:
class Matrix # I defined it so that people can test the code right away
def initialize
#rows = []
self
end
def build rows = 0, columns = 0 #Use your own version of build instead
# It should modify Matrix instead of returning a new one
#rows = Array.new(rows, Array.new(columns, 0))
end
end
class TOMatrix < Matrix
def initialize
super.build 8, 8 #Initializes Matrix and calls build on it
end
end

Useful alternative uses for yield beyond being an anonymous method?

Example:
a = Proc.new do
b = 'hey'
end
def a_method
yield
end
a_method(&a) #=> 'hey'
I understand that yield (or block.call) can be used as a simple co-routine, but I was wondering, are there any more (practical) uses for it beyond simply getting the return value from it? Can we get some of the local variables from the proc into the main method etc.?
If you're not picky about the return value of the block, you can do it using binding.
a = Proc.new do
b = 'hey'
binding
end
def a_method
new_binding = yield
p new_binding.eval("b")
end
a_method(&a)
# => "hey"
If you're using a version of Ruby >= 2.1.0, you can avoid the ickiness of eval by using local_variable_get instead:
p new_binding.local_variable_get(:b)
There are a lot of uses for Procs. For example, you can use it to give instructions to an object. I made a bubble sort using a proc that tells it how to sort. Here it is monkey patched in the array class:
class Array
def bubble_sort(&prc)
self.dup.bubble_sort!(&prc)
end
def bubble_sort!(&prc)
return self if count <= 1
prc = Proc.new { |x, y| x <=> y } unless prc.class == Proc
(0...self.count).each do |x|
((x + 1)...self.count).each do |y|
self[x], self[y] = self[y], self[x] if prc.call(self[x], self[y]) == 1
end
end
self
end
end

Lazy evaluation of infinite enumerator in Ruby 1.9 - calling instance method on object from different class

I'm trying to get to grips with Lazy Evaluation of an enumerator using Ruby 1.9. This is work in progress so will probably have other bugs/missing code but I have one specific problem right now. I'm trying to pass this test (note I cannot change the test):
def test_enumerating_with_a_single_enumerator
enumerator = SomeClass.new(some_infinite_sequence.to_enum)
assert_equal [1, 2, 3, 4, 5], enumerator.take(5)
end
I've written this code below and I know the problem is that I'm calling the lazy_select instance method from the SomeClass on the argument from the initialize method which is an instance of the Enumerator class, so I get a NoMethodError. Any suggestions? Thank you.
class SomeClass < Enumerator
def initialize(*enumerators)
super() do |yielder|
enumerators.each do |enumerator|
enumerator.lazy_select { |yielder, first_value, second_value| yielder.yield first_value if (first_value <=> second_value) <= 0 }
.first(20)
end
end
end
def lazy_select(&block)
self.class.new do |yielder|
each_cons(2) do |first_value, second_value|
block.call(yielder, first_value, second_value)
end
end
end
end
I have one specific problem right now. I'm trying to pass this test
(note I cannot change the test):
def test_enumerating_with_a_single_enumerator
enumerator = SomeClass.new(some_infinite_sequence.to_enum)
assert_equal [1, 2, 3, 4, 5], enumerator.take(5)
end
class SomeClass < Enumerator
def initialize(enum, &block)
super() do |y|
begin
enum.each do |val|
if block
block.call(y, val) #while initializing sc2 in Line B execution takes this branch
else
y << val #while initializing sc1 from Line A execution halts here
end
end
rescue StopIteration
end
end
end
def lazy_take(n)
taken = 0
SomeClass.new(self) do |y, val| #Line B
if taken < n
y << val
taken += 1
else
raise StopIteration
end
end
end
def take(n)
lazy_take(n).to_a
end
end
sc1 = SomeClass.new( (1..6).cycle ) #Line A
p sc1.take(10)
--output:--
[1, 2, 3, 4, 5, 6, 1, 2, 3, 4]
sc2 is the name I'm giving to the anonymous instance created inside lazy_take().
The code is very difficult to understand. The code sets things up so that sc1's enumerator is cycle, and sc2's enumerator is sc1 (initialize() requires that the first arg be an Enumerator). When sc1 is initialized, the code starts stepping through the values in cycle and halts at this line:
y << val
Then when lazy_take() is called, sc2 is created, and its initialization code starts stepping through the values in sc1. But there are no values in sc1, so sc1 executes the line:
y << val
to inject a value from cycle into sc1's yielder. Then sc1's yielder immediately yields the val to sc2--because in sc2's code the each() method is demanding a value from sc1. sc2 then takes val and injects it into sc2's yielder. Then the next iteration of the each block in sc2 takes place, and once again sc2's code demands a value from sc1. sc2 repeatedly demands a value from sc1, which causes sc1 to pass on a value retrieved from cycle. Once sc2 runs the loop n times, it stops demanding values from sc1. The next step is to make sc2 give up the values in it's yielder.
If you prefer, you can define initialize() like this:
def initialize(enum)
super() do |y|
begin
enum.each do |val|
if block_given?
yield y, val #while initializing sc2 in Line B execution takes this branch
else
y << val #while initializing sc1 from Line A execution halts here
end
end
rescue StopIteration
end
end
end
That shows that you do not have to specify a block parameter and explicitly call() the block. Instead, you can dispense with the block parameter and call yield(), and the values will be sent to the block automatically.
Thanks for the comments received above. They were very helpful. I've managed to solve it as follows:
class SomeClass < Enumerator
class SomeOtherClass < RuntimeError
attr_reader :enumerator
def initialize(enumerator)
#enumerator = enumerator
end
end
def initialize(*enumerators)
super() do |yielder|
values = []
enumerators.each do |enumerator|
values.push lazy_select(enumerator) { |value| sorted? enumerator }.take(#number_to_take)
end
values.flatten.sort.each { |value| yielder.yield value }
end
end
def lazy_select(enumerator, &block)
Enumerator.new do |yielder|
enumerator.each do |value|
yielder.yield value if block.call enumerator
end
end
end
def sorted?(enumerator)
sorted = enumerator.each_cons(2).take(#number_to_take).all? { |value_pair| compare value_pair }
sorted || raise(SomeClass::SomeOtherClass, enumerator)
end
def compare(pair)
pair.first <= pair.last
end
def take(n)
#number_to_take = n
super
end
end
This passes all my tests.

Is there a way to redefine []=+ in ruby

I am trying to write a simple DSL (against Redis) and I would like to define []+= myself
I have
def []=(key,val)
#redis.zadd(#name,val,key)
end
and I would like to define
def []+=(key,val)
#redis.zincrby(#name,val,key)
end
But my understanding is that Ruby provides the "[]+=" operator automaticallygiven []=
Is there a way to over-ride this behavior
Obviously I don't want this because I would not be able to, say, run this in pipeline mode
No, <operator>= can not be redefined in Ruby.
You can try to get really fancy and wrap your return values in classes that delegate to the actual value. This way, they behave like the actual value, but you can play tricks, for instance with +.
Here's a simple example:
require 'delegate'
module Redis
class Set
class Value < SimpleDelegator
def +(val)
Increment.new(self, val)
end
end
class Increment < SimpleDelegator
attr_reader :increment
def initialize(source, increment)
super(source.__getobj__ + increment)
#increment = increment
end
end
def [](key)
Value.new(#redis.not_sure_what(#name, key))
end
def []=(key,val)
if val.is_a?(Increment)
#redis.zincrby(#name,val.increment,key)
else
#redis.zadd(#name,val,key)
end
end
end
end
This is just a starting point. You'll have to be more careful than this, for example by checking the key is the same. In my simplistic example, redis[:foo] = redis[:bar] + 1 would actually be equivalent to redis[:foo] += 1...
Nope. x[y] += z expands to exactly x[y] = x[y] + z:
class << (object = Object.new)
def [](key)
puts "[#{key.inspect}]"
key
end
def []=(key, value)
puts "[#{key.inspect}] = #{value.inspect}"
value
end
end
# These are all equivalent
object['See?'] += " It's impossible."
object['See?'] = object['See?'] + " It's impossible."
object.[]=('See?', object.[]('See?').+(" It's impossible."))
# They all produce the same output:
# ["See?"]
# ["See?"] = "See? It's impossible."
# => "See? It's impossible."
You will have to create a separate method.

Resources