Ruby: recursive method - ruby

def reverse_append(arr, n)
return arr if n < 0
reverse_append(arr, n-1)
arr << n
arr
end
reverse_append([],4) #=> [0, 1, 2, 3, 4]
I can't seem to understand this recursive method. It produces an array from 0 up to n.
Can someone explain this to me?

The method reverse_append([],4) is called
Since 4 >= 0, the return statement does not get called.
The method reverse_append([],3) is called.
Since 3 >= 0, the return statement does not get called.
The method reverse_append([],2) is called.
Since 2 >= 0, the return statement does not get called.
The method reverse_append([],1) is called.
Since 1 >= 0, the return statement does not get called.
The method reverse_append([],0) is called.
Since 0 >= 0, the return statement does not get called.
The method reverse_append([],-1) is called.
Since -1 < 0, the array ([]) is returned.
We pop up one level in our call stack, to where n = 0 and arr = [].
arr << n and arr is returned, so now arr = [0].
We pop up one level in our call stack, to where n = 1 and arr = [0].
arr << n and arr is returned, so now arr = [0, 1].
We pop up one level in our call stack, to where n = 2 and arr = [0, 1].
arr << n and arr is returned, so now arr = [0, 1, 2].
We pop up one level in our call stack, to where n = 3 and arr = [0, 1, 2].
arr << n and arr is returned, so now arr = [0, 1, 2, 3].
We pop up one level in our call stack, to where n = 4 and arr = [0, 1, 2, 3].
arr << n and arr is returned, so now arr = [0, 1, 2, 3, 4].
Finally, the "top-level" method returns, and we have our final result.

Well step through the code with the supplied parameters. The first step is to check if n < 0 which its not. If it isn't 0 reverse append with [], 3 and appends the that array the number and then returns the array.
So it takes the array, adds 4 to it after it has gone through the step of dealing with [], 3, [], 2, [],1 and [], 0. So the first call that will succeed is just returning the array when it gets below 0, next is 0 gets appended, then one, then 2, then 3 and lastly the original call with 4 gets added arr << n.

There's a nice tool you can add to many editors called "Seeing Is Believing", which lets you see what is happening as code runs:
def reverse_append(arr, n)
return arr if n < 0 # => false, false, false, false, true
reverse_append(arr, n-1) # => [], [0], [0, 1], [0, 1, 2]
arr << n # => [0], [0, 1], [0, 1, 2], [0, 1, 2, 3]
arr # => [0], [0, 1], [0, 1, 2], [0, 1, 2, 3]
end
reverse_append([], 3) # => [0, 1, 2, 3]
However, with a name like "reverse_append" it seems like you should see a result that is descending in values:
def reverse_append(arr, n)
return arr if n < 0 # => false, false, false, false, true
reverse_append(arr, n-1) # => [], [0], [1, 0], [2, 1, 0]
arr.unshift n # => [0], [1, 0], [2, 1, 0], [3, 2, 1, 0]
arr # => [0], [1, 0], [2, 1, 0], [3, 2, 1, 0]
end
reverse_append([], 3) # => [3, 2, 1, 0]
In either case, there are a lot of easier ways to generate such an array without relying on recursion:
[*0..3] # => [0, 1, 2, 3]
(0..3).to_a # => [0, 1, 2, 3]
[*0..3].reverse # => [3, 2, 1, 0]
(0..3).to_a.reverse # => [3, 2, 1, 0]

Related

Find count of sub arrays of zeros in an array

I need to find the count of sub arrays of zeros in an array:
array = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1]
Result should be: 3 because we have 0, 0, 0 and 0, 0, 0.
Counting the number of zeros (6) will not work.
array.join.squeeze('0').count('0')
#=> 3
We have
s = array.join
#=> "11100111101110001"
t = s.squeeze('0')
#=> "11101111011101"
t.count('0')
#=> 3
Note one could squeeze all the characters, not just the zeroes (squeeze as opposed to squeeze('0')).
Another way:
array = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0]
array.select.with_index(1) { |n,i| n.zero? && array[i] != 0 }.size
#=> 3
When, as here, the last element of array is a zero, array[i] #=> nil when i = array.size (since i goes from 1 to array.size).
chunk_while and count might work:
array
.chunk_while(&:==) # [[1, 1, 1], [0, 0], [1, 1, 1, 1], [0], [1, 1, 1], [0, 0, 0], [1]]
.count { |arr| arr.include?(0) } # 3
Or join, scan and length:
arr
.join # "11100111101110001"
.scan(/(0+)/) # [["00"], ["0"], ["000"]]
.length # 3
You can use Enumerable#chunk and Enumerable#count to handle this as well like so:
Option 1:
arr = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1]
arr.chunk(&:zero?).count(&:first)
#=> 3
chunk will group consecutive elements together by their return value in the form of [return_value, [*elements]] so in this case chunk(&:zero?) will create groups like so
arr.chunk(&:zero?).to_a
#=> [[false, [1, 1, 1]],
# [true, [0, 0]],
# [false, [1, 1, 1, 1]],
# [true, [0]],
# [false, [1, 1, 1]],
# [true, [0, 0, 0]],
# [false, [1]]]
However chunk does not create a new Array (thus the to_a above to show the structure) instead it creates an Enumerator that stores this block of code and evaluates the return value upon subsequent method calls.
In this case we are then calling count(&:first) since count will only count the elements for which the block returns a truthy value (not nil or false).
Option 2:
arr.chunk {|e| e.zero? || nil}.count
#=> 3
Very similar to Option 1 this works because chunk will drop all elements where the return value is nil meaning
arr.chunk {|e| e.zero? || nil}.to_a
#=> [[true, [0, 0]], [true, [0]], [true, [0, 0, 0]]]
Bonus: (just for fun in case you need to count other consecutive elements)
groups = arr.chunk(&:itself)
.each_with_object(Hash.new {|h,k| h[k] =[]}) do |(e,arr),obj|
obj[e] << arr
end
#=> {1=>[[1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1]],
# 0=>[[0, 0], [0], [0, 0, 0]]}
groups[0].size
#=> 3
Yet another way...
array.each_with_object([]){ |a, ary| ary << a unless ary.last == a }.count(&:zero?)
I am providing this solution to understand the basic solution's idea of this problem.
This is c++ solution for counting number of 0 sub-array. Increment result when we get a 0. And when we get more than one zero then don't count for them.
int n = arr.size();
int cnt = 0;
for(int i = 0; i < n; i++) {
if(i > 0 && arr[i] == arr[i-1]) {
continue;
}
if(arr[i] == 0) {
cnt++;
}
}
cout << cnt << endl;

recursive method in ruby [duplicate]

This question already has answers here:
Ruby: recursive method
(3 answers)
Closed 6 years ago.
def append(arr, n)
return arr if n < 0
puts "n1: #{n}, #{arr}"
append(arr, n-1)
puts "n2: #{n}, #{arr}"
arr << n
puts "n3: #{n}, #{arr}"
arr
end
append([],4) #=> [0, 1, 2, 3, 4]
I can't seem to understand this recursive method. It produces an array from 0 up to n.
I added a few puts to see how the arr and n acted.
n1: 4, []
n1: 3, []
n1: 2, []
n1: 1, []
n1: 0, []
n2: 0, []
n3: 0, [0]
n2: 1, [0]
n3: 1, [0, 1]
n2: 2, [0, 1]
n3: 2, [0, 1, 2]
n2: 3, [0, 1, 2]
n3: 3, [0, 1, 2, 3]
n2: 4, [0, 1, 2, 3]
n3: 4, [0, 1, 2, 3, 4]
I understand how n counts down to -1, and then the arr is returned. What is confusing me is where the counting up occurs. It seems to me that when append(arr, -1) is called, the arr is returned and the loop will stop and all I get is []. So why does the method continues to run after the return is called? And where does the counting up occur?
return returns from the current method, not from all recursive calls. That said, after first return the control flow goes to the line, next to call to append. For the n=2 the backtrace will be as follows:
main ⇒ append # n = 2
main ⇒ append ⇒ append # n = 1
main ⇒ append ⇒ append ⇒ append # n = 0, return to:
main ⇒ append ⇒ append # here n = 1, continue execution till end
main ⇒ append # here n = 2, continue execution till end
main # the topmost recursion level returned to main

Recursion involving an Array for Wonky Coins

Here's the prompt I've been given:
Catsylvanian money is a strange thing: they have a coin for every
denomination (including zero!). A wonky change machine in
Catsylvania takes any coin of value N and returns 3 new coins,
valued at N/2, N/3 and N/4 (rounding down).
Write a method wonky_coins(n) that returns the number of coins you
are left with if you take all non-zero coins and keep feeding them
back into the machine until you are left with only zero-value coins.
Difficulty: 3/5
describe "#wonky_coins" do
it "handles a simple case" do
wonky_coins(1).should == 3
end
it "handles a larger case" do
wonky_coins(5).should == 11
# 11
# => [2, 1, 1]
# => [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
# => [[[0, 0, 0], 0, 0], [0, 0, 0], [0, 0, 0]]
end
it "handles being given the zero coin" do
wonky_coins(0).should == 1
end
end
Maybe it's because of the tests given that involve arrays, but I couldn't get my mind off of them! So my solution so far is as follows:
def wonky_coins(n)
arr = []
arr << n/2 << n/3 << n/4
#base case?
if arr.all?{|coin| coin == 0}
return arr.flatten.length
else
arr.map{|x| wonky_coins(x)}
end
end
p wonky_coins(5)
Except I get [[3,3,3],3,3] as an output if I map it. It's not actually recurring, but even before that, it's giving a strange output that I can't for the life of me understand why the output is this way!
I know it's because I'm using the map method, is it because I'm mutating it while iterating it through wonky_coins again that I'm getting this strange output I can't explain?
I've since looked at the solution and realized that arrays made it needlessly complicated, but I'm still wondering what's going on here??
Here's what Seeing is Believing shows as the code runs:
def wonky_coins(n)
arr = [] # => [], [], [], [], [], [], []
arr << n/2 << n/3 << n/4 # => [2, 1, 1], [1, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]
#base case?
if arr.all?{|coin| coin == 0} # => false, false, true, true, true, true, true
return arr.flatten.length # => 3, 3, 3, 3, 3
else
arr.map{|x| wonky_coins(x)} # => [3, 3, 3], [[3, 3, 3], 3, 3]
end
end
p wonky_coins(5) # => [[3, 3, 3], 3, 3]
# >> [[3, 3, 3], 3, 3]
Seeing is Believing is a great tool and can help dig out weirdness in code.

Generating combinations from an array which == a specified amount?

I need to get all the possible number combinations from denom_arr which equal the amt.
denom_arr = [4,3,1]
amt = 10
This case would produce:
[4, 4, 1, 1]
[3, 3, 3, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[4, 3, 1, 1, 1]
[4, 3, 3]
. . . (other cases...)
Problem is the code I wrote is breaking after 1-3 and I'm not sure how to make it loop over the same index to get case 4-6+
set, sets = [], []
i = 0
loop do
i = 0 if denom_arr[i].nil?
loop do
set << denom_arr[i]
break if set.inject(:+) > amt
end
set.pop if set.inject(:+) > amt
if set.inject(:+) == amt
sets << set
set = []
denom_arr.shift
end
i += 1
sets
break if denom_arr.empty?
end
UPDATE
I know this can be done with recursion with memoization/dynamic programming techniques, but I am trying to do this strictly in a loop for the sake of testing a theory.
I would do this recursively
def possible_sums(arr, amt)
return [[]] if amt == 0
return [] if amt < 0
arr.reduce([]) do |sums, e|
sums.concat(
possible_sums(arr, amt-e)
.map { |sum| sum.unshift(e).sort }
)
end.uniq
end
p possible_sums([4,3,1], 10)
# => [
# [1, 1, 4, 4], [3, 3, 4], [1, 1, 1, 3, 4], [1, 1, 1, 1, 1, 1, 4],
# [1, 3, 3, 3], [1, 1, 1, 1, 3, 3], [1, 1, 1, 1, 1, 1, 1, 3],
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
# ]
Although this is potentially inefficient in that it repeats work, this can be alleviated by using dynamic programming (essentially, memoizing the results of the recursive function).
UPDATE Here is an iterative solution:
def possible_sums_it(arr, amt)
sums = Array.new(amt+1) { [] }
sums[0] << []
(1..amt).each do |i|
arr.each do |e|
if i-e >= 0
sums[i].concat(
sums[i-e].map { |s| [e, *s].sort }
)
end
end
sums[i].uniq!
end
sums[amt]
end
This is in fact the dynamic programming algorithm for the problem.
So if you squint at it just right, you'll see that essentially what it is doing, is calculating all the possible sums for 0 up to amt into the sums array, using what is basically the recursive algorithm, but instead of the recursive call, we lookup a value in sums that we have calculated beforehand.
This works because we know that we won't need sums[i] before sums[j] for j < i.

Build efficient array integer incrementer with different caps per number

I want to program a counter which is represented by an array of numbers, starting with:
[0, 0, 0]
The constraint here is, that each position has a different cap, so it's not necessarily 9 or something else, but it is given. For instance:
[4, 2, 1]
Which would lead to the following incrementation sequence:
[0, 0, 0]
[0, 0, 1]
[0, 1, 0]
[0, 1, 1]
[0, 2, 0]
[0, 2, 1]
[1, 0, 0]
.
.
.
Of course I can think of a solution using modulo and adding each carryover onto the next position. But has someone an idea how to implement this efficiently, respectively with nice Ruby syntax without cluttering it too much?
That is my naive implementation:
max = [10, 1, 1, 1, 10]
counter = [0, 0, 0, 0, 0]
i = counter.length-1
while counter != max do
counter[i] = counter[i] + 1
while counter[i] > max[i]
counter[i] = 0
i = i - 1
counter[i] = counter[i] + 1
end
i = counter.length-1
end
I'm not sure about efficiency but here's my shot at it:
start = [0, 0, 0]
cap = [4, 2, 1]
start.zip(cap).map{ |i, c| (i..c).to_a }.reduce(&:product).map &:flatten
Produces something like:
[[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[0, 1, 1],
[0, 2, 0],
[0, 2, 1],
[1, 0, 0],
[1, 0, 1],
[1, 1, 0],
[1, 1, 1],
[1, 2, 0],
[1, 2, 1],
[2, 0, 0],
[2, 0, 1]...]
Edit: I was writing this before you made your edit. It seemed like you wanted a counter object, not just to output a list.
1) I would recommend specifying not the limits but (limit+1) of each of the digits. For example, for a [second, minute, hour, day, year] counter it makes more sense (to me) to write [60, 60, 24, 365] instead of [59,59,23,364].
2) You'll have to figure out what to do if your counter overflows the last limit of your array. I added an extra position that counts to infinity.
3) I would also recommend reversing the order of the array, at least in the internal representation to avoid inverting subscripts. If you don't want it like that, you can .reverse the bases in initialize and #digits in to_s
class MyCounter
def initialize bases
#bases = bases
#bases << 1.0/0 # Infinity
#digits = Array.new(bases.size, 0)
prod = 1
#digit_values = [1] + #bases[0..-2].map { |b| prod *= b }
end
attr_reader :digit_values
def to_s
#digits
end
def increment(digit=0)
v = #digits[digit] + 1
if v < #bases[digit]
#digits[digit] = v
else
#digits[digit] = 0
increment(digit+1)
end
self
end
def +(integer)
(#digits.size - 1).step(0,-1).each do |i|
#digits[i] += integer / #digit_values[i]
integer = integer % #digit_values[i]
end
self
end
end
c1 = MyCounter.new [2,3,5]
20.times { c1.increment; p c1 }
c2 = MyCounter.new [2,3,5]
c2 += 20
p c2
Create an array for each cap, with values from 0 upto cap. Take the first array and calculate the Cartesian product with the rest of the arrays.
caps = [4, 2, 1]
arrs = caps.map{|cap| (0..cap).to_a} #=>[[0, 1, 2, 3, 4], [0, 1, 2], [0, 1]]
p arrs.shift.product(*arrs)
# =>[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [0, 2, 0], [0, 2, 1], ...
If you don't want a memory-consuming array with the results, then provide a block. product will yield each element to it, one by one.
arrs = caps.map{|cap| (0..cap).to_a}
arrs.shift.product(*arrs){|el| puts el.join} #no resulting array
#000
#001
#010
#011
#...

Resources