How does the memo in #inject work? - ruby

I'm trying to use a while loop inside #inject. However, the last memo becomes nil at some point and I don't understand why. Here is my example (I use #each on the example just to show the expected result):
class TestClass
BASE_ARRAY = [5, 1]
def test_method(value)
result = []
BASE_ARRAY.each do |item|
while item <= value
result << item
value -= item
end
end
result
end
def test_method_alternate(value)
BASE_ARRAY.inject do |memo, item|
while item <= value
p memo
# memo << item (if left, returns NoMethodError for nil Class)
value -= item
end
end
end
end
solution_one = TestClass.new.test_method(11)
p solution_one # => [5, 5, 1]
solution_two = TestClass.new.test_method_alternate(11)
p solution_two
# => []
[]
nil
How does the accumulator become nil?

You're getting nil initially from the while loop:
The result of a while loop is nil unless break is used to supply a value.
That result becomes the result of other statements in a chain:
while
-> do |memo, item|
-> BASE_ARRAY.inject
-> test_method_alternate(11)
-> solution_two
To have .inject fill up an array, you'll want to provide an empty array to use as the first memo:
BASE_ARRAY.inject([]) do |memo, item|
# ... ^^^^
Then, be sure the array is the result of the block:
... do |memo, item|
while item <= value
memo << item
value -= item
end
memo # <---
end

Two things:
You need to initialize the memo with a value, in this case, you'll want a [].
You need to return the memo on each iteration of inject.
So, you should get your desired result of [5, 5, 1] by changing your method to be like this:
def test_method_alternate(value)
BASE_ARRAY.inject([]) do |memo, item|
while item <= value
memo << item
value -= item
end
memo
end
end

Related

Why doesn't reduce return the initial value if the block never changes the accumulator?

Consider the following irb loop:
irb(main):015:0> [nil, nil].reduce(0) { |accum, x| accum + 1 unless x.nil? }
=> nil
Why does this return nil instead of 0?
According to the Ruby Enumerable documentation:
If you specify a block, then for each element in enum the block is passed an accumulator value (memo) and the element. If you specify a symbol instead, then each element in the collection will be passed to the named method of memo. In either case, the result becomes the new value for memo. At the end of the iteration, the final value of memo is the return value for the method.
My expectation would be that the accumulator should get set to 0 before the array starts to get folded, since that is given as the initial value. Then, the block's escape clause will trigger for all elements in this array, so the accumulator will never change. Finally, since 0 is the last value stored for the accumulator, it should be returned.
Whatever the block returns is going to be the next accumulator value.
And you return nil:
'whatever' unless true #=> nil
You could do this:
arr.reduce(0) { |a, e| e.nil? ? a : a + 1 }
Or this:
arr.compact.reduce(0) { |a, e| a + 1 }
Or this:
arr.compact.size
Reduce starts with the accumulator you pass but then sets it to whatever the block returns.
It might be helpful to see what it does internally (this is not the actual source code but a simple reproduction):
class Array
def my_reduce(memo, &blk)
each { |i| memo = blk.call(memo, i) }
memo
end
end
Here's some examples to show its usage:
# with 0 as starting memo
[nil, nil].reduce(0) { |memo, i| i ? memo + 1 : memo } # => 0
[nil, nil].reduce(0) { |memo, i | memo += 1 if i; memo; } # => 0
[nil, nil].reduce(0) { |memo, i| memo + (i ? 1 : 0) } # => 0
# with [] as starting memo
[1,2].reduce([]) { |memo, i| memo.push(i + 1); memo } # => [2,3]
[1,2].reduce([]) { |memo, i| memo.concat([i + 1]) } # => [2,3]
[1,2].reduce([]) { |memo, i| memo + [i + 1] } # => [2,3]
[1,2].reduce([]) { |memo, i| [*memo, i + 1] } # => [2,3]
You can see how only some of these require memo to be returned as the last line. The ones that don't are taking advantage of methods which return their modified objects, instead of relying on mutability (memo.push) or local variable assignment (memo += 1)
each_with_object is basically the same thing as reduce except that it automatically returns the accumulator from each block, and reverses the order of the block args (|i, memo| instead of |memo, i). It can be nice syntactic sugar for reduce when the memo is a mutable object. Returning the memo from the block is no longer necessary in the following example:
[1,2].each_with_object([]) { |i, memo| memo.push(i + 1) } # => [2,3]
However it won't work with your original example, because the memo (a number) is immutable:
# returns 0 but should return 1
[true, nil].each_with_object(0) { |i, memo| memo += 1 if i }
to say memo += 1 here is nothing but local variable assignment. Remember, you can never change the value of self for an object in ruby, not even a mutable one. If Ruby had increment operators (i++) then it might be a different story (see no increment operator in ruby)
Your expectations are correct but the missing piece is considering what the block returns after execution.
In Ruby the last thing to execute is returned and this code: accum + 1 unless x.nil? returns nil.
Just for science here's an example:
irb(main):051:0> puts 'ZOMG' unless nil.nil?
=> nil
Because nil is returned by the block your accumulator's initial 0 is overwritten with nil.
If you modify the code to return the accumulator you will get 0 as expected:
irb(main):052:0> [nil, nil].reduce(0) do |accum, x|
irb(main):053:1* accum + 1 unless x.nil?
irb(main):054:1> accum
irb(main):055:1> end
=> 0

ruby return inner loop value to outer loop

how do I return the value from an inner loop to the .map of an outer loop, example:
class LetterNumber
def initialize(letter, number)
#letter = letter
#number = number
end
end
do_stuff
letter_numbers = // array of LetterNumber objects
numbers = [1, 2, 3]
letter_numbers.map do |letter_number|
numbers.each do |number|
letter_number.number = letter_number.number + number
end
end
end
I want the do_stuff method to return an array of the updated LetterNumber objects. so I want the inner loop to return a letter_number object on each of its iterations to the outer loop. Is this possible with some sort of iterator instead of breaking the loops out into separate methods or something?
You can use inject method to calculate the sum directly:
class LetterNumber
def initialize(letter, number)
#letter = letter
#number = number
end
end
def do_stuff
letter_numbers = // array of LetterNumber objects
numbers = [1, 2, 3]
letter_numbers.map do |letter_number|
letter.number += numbers.inject(:+)
letter_number
end
end
or use each_with_object to return letter_number after your loop.
def do_stuff
letter_numbers = // array of LetterNumber objects
numbers = [1, 2, 3]
letter_numbers.map do |letter_number|
numbers.each_with_object(letter_number) do |number|
letter_number.number = letter_number.number + number
end
end
end

How can I pass in a block to my "bubble sort" method?

The below code is my newbie take on a bubble sort method.
#For each element in the list, look at that element and the element
#directly to it's right. Swap these two elements so they are in
#ascending order.
def bubble_sort (array)
a = 0
b = 1
until (array.each_cons(2).all? { |a, b| (a <=> b) <= 0}) == true do
sort = lambda {array[a] <=> array[b]}
sort_call = sort.call
loop do
case sort_call
when -1 #don't swap
a += 1
b += 1
break
when 0 #don't swap
a += 1
b += 1
break
when 1 #swap
array.insert(a,array.delete_at(b))
a += 1
b += 1
break
else #end of array, return to start
a = 0
b = 1
break
end
end
end
puts array.inspect
end
array = [4, 2, 5, 6, 3, 23, 5546, 234, 234, 6]
bubble_sort(array)
I want to be able to alter this method so that it takes a block of code as an argument and uses this to determine how it sorts.
For example:
array = ["hello", "my", "name", "is", "daniel"]
bubble_sort(array) {array[#a].length <=> array[#b].length}
(When I've tried this I've turned a and b into instance variables throughout the code.)
I have tried using yield but I get undefined method 'length' for nil:NilClass once the end of the array is reached. I've tried adding in things such as
if array[#b+1] == nil
#a = 0
#b = 1
end
This helps but I still end up with weird problems like infinite loops or not being able to sort more than certain amount of elements.
Long story short, I have been at this for hours. Is there a simple way to do what I want to do? Thanks.
The way you're calling your lambda is a bit odd. It's actually completely unnecessary. I refactored your code and cleaned up a bit of the redundancy. The following works for me:
def sorted?(arr)
arr.each_cons(2).all? { |a, b| (a <=> b) <= 0 }
end
def bubble_sort (arr)
a = 0
b = 1
until sorted?(arr) do
# The yield call here passes `arr[a]` and `arr[b]` to the block.
comparison = if block_given?
yield(arr[a], arr[b])
else
arr[a] <=> arr[b]
end
if [-1, 0, 1].include? comparison
arr.insert(a, arr.delete_at(b)) if comparison == 1
a += 1
b += 1
else
a = 0
b = 1
end
end
arr
end
sample_array = [4, 2, 5, 6, 3, 23, 5546, 234, 234, 6]
# Sanity check:
100.times do
# `a` is the value of `arr[a]` in our function above. Likewise for `b` and `arr[b]`.
print bubble_sort(sample_array.shuffle) { |a, b| a <=> b }, "\n"
end
EDIT
A cleaner version:
# In place swap will be more efficient as it doesn't need to modify the size of the arra
def swap(arr, idx)
raise IndexError.new("Index #{idx} is out of bounds") if idx >= arr.length || idx < 0
temp = arr[idx]
arr[idx] = arr[idx + 1]
arr[idx + 1] = temp
end
def bubble_sort(arr)
loop do
sorted_elements = 0
arr.each_cons(2).each_with_index do |pair, idx|
comparison = if block_given?
yield pair.first, pair.last
else
pair.first <=> pair.last
end
if comparison > 0
swap(arr, idx)
else
sorted_elements += 1
end
end
return arr if sorted_elements >= arr.length - 1
end
end
# A simple test
sample_array = [4, 2, 2, 2, 2, 2, 5, 5, 6, 3, 23, 5546, 234, 234, 6]
sample_str_array = ["a", "ccc", "ccccc"]
100.times do
print bubble_sort(sample_array.shuffle) { |a, b| a <=> b }, "\n"
print bubble_sort(sample_str_array.shuffle) { |a, b| a.length <=> b.length }, "\n"
end
You're not too far off. Just a few things:
Make your function take a block argument
def bubble_sort (array, &block)
Check to see if the user has provided a block
if block_given?
# Call user's comparator block
else
# Use the default behavior
end
Call the user's comparator block
block.call(a, b)
In the user-provided block, accept block params for the elements to compare
bubble_sort(array) {|a,b| a.length <=> b.length}
That should put you in the right ballpark.

Turning a method into an enumerable method

I rewrote the map method:
def my_map(input, &block)
mod_input = []
x = -1
while x < input.length - 1
x = x + 1
if block == nil
return input
break
end
mod_input.push(block.call(input[x]))
end
return mod_input
end
I need to call this code as I would call map or reverse. Does anyone know the syntax for that?
Are you asking how you put a method into a module? That's trivial:
module Enumerable
def my_map(&block)
mod_input = []
x = -1
while x < length - 1
x = x + 1
if block == nil
return self
break
end
mod_input.push(block.call(self[x]))
end
return mod_input
end
end
[1, 2, 3, 4, 5].my_map(&2.method(:*))
# => [2, 4, 6, 8, 10]
Or are you asking how to make your method an Enumerable method? That's more involved: your method currently uses many methods that are not part of the Enumerable API. So, even if you make it a member of the Enumerable module, it won't be an Enumerable method. Enumerable methods can only use each or other Enumerable methods. You use length and [] both of which are not part of the Enumerable interface, for example, Set doesn't respond to [].
This would be a possible implementation, using the Enumerable#inject method:
module Enumerable
def my_map
return enum_for(__method__) unless block_given?
inject([]) {|res, el| res << yield(el) }
end
end
[1, 2, 3, 4, 5].my_map(&2.method(:*))
# => [2, 4, 6, 8, 10]
A less elegant implementation using each
module Enumerable
def my_map
return enum_for(__method__) unless block_given?
[].tap {|res| each {|el| res << yield(el) }}
end
end
[1, 2, 3, 4, 5].my_map(&2.method(:*))
# => [2, 4, 6, 8, 10]
Note that apart from being simply wrong, your code is very un-idiomatic. There is also dead code in there.
the break is dead code: the method returns in the line just before it, therefore the break will never be executed. You can just get rid of it.
def my_map(&block)
mod_input = []
x = -1
while x < length - 1
x = x + 1
if block == nil
return self
end
mod_input.push(block.call(self[x]))
end
return mod_input
end
Now that we have gotten rid of the break, we can convert the conditional into a guard-style statement modifier conditional.
def my_map(&block)
mod_input = []
x = -1
while x < length - 1
x = x + 1
return self if block == nil
mod_input.push(block.call(self[x]))
end
return mod_input
end
It also doesn't make sense that it is in the middle of the loop. It should be at the beginning of the method.
def my_map(&block)
return self if block == nil
mod_input = []
x = -1
while x < length - 1
x = x + 1
mod_input.push(block.call(self[x]))
end
return mod_input
end
Instead of comparing an object against nil, you should just ask it whether it is nil?: block.nil?
def my_map(&block)
return self if block.nil?
mod_input = []
x = -1
while x < length - 1
x = x + 1
mod_input.push(block.call(self[x]))
end
return mod_input
end
Ruby is an expression-oriented language, the value of the last expression that is evaluated in a method body is the return value of that method body, there is no need for an explicit return.
def my_map(&block)
return self if block.nil?
mod_input = []
x = -1
while x < length - 1
x = x + 1
mod_input.push(block.call(self[x]))
end
mod_input
end
x = x + 1 is more idiomatically written x += 1.
def my_map(&block)
return self if block.nil?
mod_input = []
x = -1
while x < length - 1
x += 1
mod_input.push(block.call(self[x]))
end
mod_input
end
Instead of Array#push with a single argument it is more idiomatic to use Array#<<.
def my_map(&block)
return self if block.nil?
mod_input = []
x = -1
while x < length - 1
x += 1
mod_input << block.call(self[x])
end
mod_input
end
Instead of Proc#call, you can use the .() syntactic sugar.
def my_map(&block)
return self if block.nil?
mod_input = []
x = -1
while x < length - 1
x += 1
mod_input << block.(self[x])
end
mod_input
end
If you don't want to store, pass on or otherwise manipulate the block as an object, there is no need to capture it as a Proc. Just use block_given? and yield instead.
def my_map
return self unless block_given?
mod_input = []
x = -1
while x < length - 1
x += 1
mod_input << yield(self[x])
end
mod_input
end
This one is opinionated. You could move incrementing the counter into the condition.
def my_map
return self unless block_given?
mod_input = []
x = -1
while (x += 1) < length
mod_input << yield(self[x])
end
mod_input
end
And then use the statement modifier form.
def my_map
return self unless block_given?
mod_input = []
x = -1
mod_input << yield(self[x]) while (x += 1) < length
mod_input
end
Also, your variable names could use some improvements. For example, what does mod_input even mean? All I can see is that it is what you output, so why does it even have "input" in its name? And what is x?
def my_map
return self unless block_given?
result = []
index = -1
result << yield(self[index]) while (index += 1) < length
result
end
This whole sequence of initializing a variable, then mutating the object assigned to that variable and lastly returning the object can be simplified by using the K Combinator, which is available in Ruby as Object#tap.
def my_map
return self unless block_given?
[].tap {|result|
index = -1
result << yield(self[index]) while (index += 1) < length
}
end
The entire while loop is useless. It's just re-implementing Array#each, which is a) unnecessary because Array#each already exists, and b) means that your my_map method will only work with Arrays but not other Enumerables (for example Set or Enumerator). So, let's just use each instead.
def my_map
return self unless block_given?
[].tap {|result|
each {|element|
result << yield(element)
}
}
end
Now it starts to look like Ruby code! What you had before was more like BASIC written in Ruby syntax.
This pattern of first creating a result object, then modifying that result object based on each element of a collection and in the end returning the result is very common, and it even has a fancy mathematical name: Catamorphism, although in the programming world, we usually call it fold or reduce. In Ruby, it is called Enumerable#inject.
def my_map
return self unless block_given?
inject([]) {|result, element|
result << yield(element)
}
end
That return self is strange. map is supposed to return a new object! You don't return a new object, you return the same object. Let's fix that.
def my_map
return dup unless block_given?
inject([]) {|result, element|
result << yield(element)
}
end
And actually, map is also supposed to return an Array, but you return whatever it is that you called map on.
def my_map
return to_a unless block_given?
inject([]) {|result, element|
result << yield(element)
}
end
But really, if you look at the documentation of Enumerable#map, you will find that it returns an Enumerator and not an Array when called without a block.
def my_map
return enum_for(:my_map) unless block_given?
inject([]) {|result, element|
result << yield(element)
}
end
And lastly, we can get rid of the hardcoded method name using the Kernel#__method__ method.
def my_map
return enum_for(__method__) unless block_given?
inject([]) {|result, element|
result << yield(element)
}
end
Now, that looks a lot better!
class Array
def my_map(&block)
# your code, replacing `input` with `self`
end
end
The code itself is not really idiomatic Ruby - while is very rarely used for iteration over collections, and if you don't need to pass a block somewhere else, it is generally cleaner to use block_given? instead of block.nil? (let alone block == nil), and yield input[x] instead of block.call(input[x]).

How to replace element in multidimensional array in ruby

I am having some difficulty with my code and I am hoping for some insight:
I have a 2d array for a board and I am attempting to replace a number with "X" when called, but am having struggles achieving this.
class BingoBoard
def initialize
#bingo_board = Array.new(5) {Array (5.times.map{rand(1..100)})}
#bingo_board[2][2] = 'X'
end
def new_board
#bingo_board.each{|row| p row}
end
def ball
#letter = ["B","I","N","G","O"].shuffle.first
#ball = rand(1..100)
puts "The ball is #{#letter}#{#ball}"
end
def verify
#ball
#bingo_board.each{|row| p row}
#bingo_board.collect! { |i| (i == #ball) ? "X" : i}
end
end
newgame = BingoBoard.new
puts newgame.ball
newgame.verify
I am aware that when verify is called it is iterating only through the array1, but I am unsure how to go about making the fix. Any help appreciated.
This is the root of the problem:
#bingo_board.collect! { |i| (i == #ball) ? "X" : i}
In this example, i is an array. So what you might want to do is to replace your code with something like:
#bingo_board.collect! do |i| # you're iterating over a double array here
if i.include?(#ball) # i is a single array, so we're checking if the ball number is included
i[i.index(#ball)] = 'X'; i # find the index of the included element, replace with X
else
i
end
end
Or if you prefer one-liner:
#bingo_board.collect! { |i| i.include?(#ball) ? (i[i.index(#ball)] = 'X'; i) : i }
Be aware that this is going to only replace the first occurrence of the element. So, say if your ball is 10, and you have:
[8, 9, 9, 10, 10]
you will get:
[8, 9, 9, "X", 10]
If you want ALL of the 10s to be replaced, then do something like:
#bingo_board.collect! do |i|
if i.include?(#ball)
i.collect! { |x| x == #ball ? 'X' : x }
else
i
end
end

Resources