Related
trio = Proc.new do |x|
tf = true
puts x
if tf
puts "ai yo"
end
end
trio.call([1, 2, 3, 4, 5])
output:
1 2 3 4 5 ai yo #its only doing the ai yo part only once when I believe it
should do it after every number
but What I am expecting of output is:
1 ai yo 2 ai yo 3 ai yo 4 ai yo 5 ai yo
I still cant wrap my head around why this is happening.
Im trying to get this program to work that i thought would be a cool way to use procs although in this specific problem i dont need to basically:
#The prime factors of 13195 are 5, 7, 13 and 29.
#What is the largest prime factor of the number 600851475143 ?
number = 13195
def factorsAndOptions(num, proc = Proc.new {|x|return x})
factorsArray = []
for i in 1..num
factorsArray.push(i) if num % i == 0
end
proc.call(factorsArray)
end
largestPrime = Proc.new do |x|
prime = true
for j in 2...x
if (x % x == 0)
prime = false
end
end
larger = x if prime && larger > x
puts larger
larger
end
factorsAndOptions(number, largestPrime)
call won't iterate over arguments. What you've written is, effectively:
puts [1, 2, 3, 4, 5]
puts "ai yo"
If you want to iterate, use each:
[1, 2, 3, 4, 5].each(&trio)
Or:
[1, 2, 3, 4, 5].each { |i| trio.call(i) }
As has been mentioned, you do not have any looping block. Your proc - trio is acting on the whole array as one single element.
In your example: x becomes [1, 2, 3, 4, 5] and not individual elements of the array as you are expecting.
To circumvent this you can either loop inside your Proc or create a separate Proc that will loop over the elements of the array and call the first Proc.
Example 1
trio = Proc.new do |arr|
arr.each do |elem|
puts elem
if tf
puts "ai yo"
end
end
end
This assumes that arr is an array
Example 2
trio = Proc.new do |x|
tf = true
puts x
if tf
puts "ai yo"
end
end
trio_helper = Proc.new do |x|
arr = x.to_a
arr.each do |elem|
trio.call(elem)
end
end
trio_helper.call([1, 2, 3, 4, 5])
This utilizes the original Proc you have written and uses another Proc to iterate over the array and call the first one on each element.
Haven't seen this one before, but I was wondering how you can find the sums of both diagonals of a 2D array in Ruby. Say you have a simple array, with 3 rows and 3 columns.
array = [1,2,3,4,5,6,7,8,9]
I can break it into groups of three by using
array.each_slice(3).to_a
Would now be
[1,2,3], [4,5,6], [7,8,9]
[1,2,3]
[4,5,6]
[7,8,9]
In this case, the diagonals are
1 + 5 + 9 = 15
3 + 5 + 7 = 15
So the total sum would be 15 + 15 = 30
I was thinking I could do something like
diagonal_sum = 0
for i in 0..2
for j in 0..2
diagonal_sum += array[i][j]
end
end
Here is my try :
array = [1,2,3,4,5,6,7,8,9]
sliced = array.each_slice(3).to_a
# As sliced size is 3, I took 2, i.e. 3 - 1
(0..2).map { |i| sliced[i][i] } #=> [1, 5, 9]
(0..2).map { |i| sliced[i][-i-1] } # => [3, 5, 7]
(0..2).map { |i| sliced[i][i] }.reduce :+
# => 15
(0..2).map { |i| sliced[i][-i-1] }.reduce :+
# => 15
As per the above observation it seems in one iteration you can do solve :
left_diagonal, right_diagoal = (0..2).each_with_object([[], []]) do |i, a|
a[0] << sliced[i][i]
a[1] << sliced[i][-i-1]
end
left_diagonal.reduce(:+) # => 15
right_diagonal.reduce(:+) # => 15
Added, OOP style of code :
class SquareMatrix
attr_reader :array, :order
def initialize array, n
#array = array.each_slice(n).to_a
#order = n
end
def collect_both_diagonal_elements
(0...order).collect_concat { |i| [ array[i][i], array[i][-i-1] ] }
end
def collect_left_diagonal_elements
(0...order).collect { |i| array[i][i] }
end
def collect_right_diagonal_elements
(0...order).collect { |i| array[i][-i-1] }
end
def sum_of_diagonal_elements type
case type
when :all then collect_both_diagonal_elements.reduce(0, :+)
when :right then collect_right_diagonal_elements.reduce(0, :+)
when :left then collect_left_diagonal_elements.reduce(0, :+)
end
end
end
array = [1,2,3,4,5,6,7,8,9]
sqm = SquareMatrix.new array, 3
sqm.collect_both_diagonal_elements # => [1, 3, 5, 5, 9, 7]
sqm.sum_of_diagonal_elements :all # => 30
sqm.collect_left_diagonal_elements # => [1, 5, 9]
sqm.sum_of_diagonal_elements :left # => 15
sqm.collect_right_diagonal_elements # => [3, 5, 7]
sqm.sum_of_diagonal_elements :right # => 15
The following is mostly for the academic discussion:
For the main diagonal, you are looking for the "Trace" function which is defined for the "Matrix" class. So the following will work (although it doesn't get you the other diagonal and I wouldn't bet on its efficiency):
require 'Matrix'
a = array.each_slice(3).to_a
Matrix[*a].trace
To get the other diagonal you have to somehow "flip" the matrix, so the following seems to work (Since the result of each_slice is an array of rows, reverse reverses the order of the row. Reversing the order of the columns is more difficult):
Matrix[*a.reverse].trace
I totally forgot about #map.with_index ...Thanks to #xlembouras , heres a one-liner
first_diagonal = array.map.with_index {|row, i| row[i]} .inject :+
inverted_diagonal = array.map.with_index {|row, i| row[-i-1]} .inject :+
It's possible to make it a one-liner:
first_diagonal, inverted_diagonal = (array.map.with_index {|row, i| row[i]} .inject :+) , (array.map.with_index {|row, i| row[-i-1]} .inject :+)
Original:
Here's a thought, which makes me think it would be great to have a #map_with_index method:
for a first to last diagonal:
i = -1
array.map { |row| row[i=i+1] }.inject :+
for the last to first diagonal (assuming a square array):
i = array.length
array.map { |row| row[i=i-1] }.inject :+
a = [1,2,3,4,5,6,7,8,9]
p a.values_at(0,2,4,4,6,8).inject(&:+) #=> 30
I would try iterating through the array and keep the values that I need according to the length of the (grouped) array
array = [[1,2,3], [4,5,6], [7,8,9]]
dimension = array.length
array.flatten.map.with_index do |x,i|
x if [0, dimension - 1].include?(i % dimension)
end.compact.inject(:+)
#=> 30
You don't need to first apply slice:
arr = [1,2,3,4,5,6,7,8,9]
We visualize arr as:
1 2 3
4 5 6
7 8 9
n = Math.sqrt(arr.size).round
#=> 3
For the main diagonal:
(0...arr.size).step(n+1).reduce(0) { |t,i| t+arr[i] }
#=> 15
For the off-diagonal:
(n-1..arr.size-n).step(n-1).reduce(0) { |t,i| t+arr[i] }
#=> 15
Another example:
arr = [1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6]
1 2 3 4
5 6 7 8
9 0 1 2
3 4 5 6
n = Math.sqrt(arr.size).round
#=> 4
(0...arr.size).step(n+1).reduce(0) { |t,i| t+arr[i] } +
(n-1..arr.size-n).step(n-1).reduce(0) { |t,i| t+arr[i] }
#=> 14 + 14 => 28
require 'Matrix'
arr = [[1, 3, 4], [2, 5, 7], [6, 7, 8]]
diag1 = Matrix[*arr].tr
diag2 = Matrix[*arr.reverse].tr
def diagonal(array)
single=array.flatten
new=[]
i=array.length-1
while i < single.length-2
new << single[i]
i+=array.length-1
end
new.sum
end
p diagonal([
[1, 2, 3],
[4, 5, 6],
[7, 9, 8],
])
OUTPUT
15
That is for finding the sum of right diagonal of a 2D array
I'm running through TestFirstRuby and I've been stuck on problem 12, building a Reverse Polish Notation calculator. I've gotten through all of the tests except for the last one, asking me to take a string ("1 2 3 * +" and then "1 2 3 * + 4 5 / -"), and then evaluate the expression.
What I'm trying to do is convert the string to an array, changing the numbers into integers and the operators into symbols, then go through the array and evaluate the expression anytime it comes to an operator.
Here's the relevant part of the code:
def initialize
#expression = ''
end
def tokens(string)
tokens = string.split(' ')
tokens.map! { |digit| /[0-9]/.match(digit) ? digit.to_i : digit.to_sym }
end
def evaluate(string)
#1 2 3 * +
#1 2 3 * + 4 5 - /
total = 0
#expression = tokens(string)
#expression.map!{|item|
index = #expression.index(item)
if item == :+
total = total + (#expression[index-1] + #expression[index-2])
2.times {#expression.delete_at(index-1)}
elsif item == :-
total = total + (#expression[index-1] - #expression[index-2])
2.times {#expression.delete_at(index-1)}
elsif item == :x
total = total + (#expression[index-1] * #expression[index-2])
2.times {#expression.delete_at(index-1)}
elsif item == :/
total = total + (#expression[index-1].to_f / #expression[index-2])
2.times {#expression.delete_at(index-1)}
end
}
total
end
What I WANT to happen is this: for each item in the array, it checks if it matches any of the symbols. If there's a match, it changes the element two spaces back from the symbol into the value of whatever the expression is (so 2 3 * becomes 5 3 *). Then, I"m trying to delete the operator and the integer immediately before it, leaving only the evaluated value. I'm doing this by running delete_at twice on the index just before the operator (ideally, 5 3 * goes to 5 * and then just 5). Then it'll move on to the next element in the array.
What I THINK is going wrong, and am having trouble fixing: I think something's going on with the variable scope here. I'm trying to get the expression to be permanently changed every time it runs the code on whatever element it's currently on in the each loop. For each element, a variable 'index' is set using #expression.index(item). This should reset for each element in the each loop. I THINK what's going on is that the original #expression array is being called on for each iteration of the each loop, unchanged from each iteration of the each loop.
The error I'm getting is saying that when it gets to the '+' at the end of the first test string ('1 2 3 * +'), it's trying to add using :x, meaning that when it's calling for the two variables to add together (#expression[index-1] + #expression[index-2]), it's pulling the symbol, which I thought should have been deleted from #expression already. So what I'm hoping will evaluate as 6 + 1 is being evaluated as 3 + :x, which wouldn't work. It's pulling elements from the original array, instead of pulling from the array as it's changed.
Hopefully I'm explaining this clearly enough. Any advice would be great. I'm thinking there's something with scope going on, but I can't find anything specific to this kind of problem to help me out. I've tried different ways of coding this (.map, .each_with_index, .map.with_index, and others), and I'm getting the same problem each time.
You have a tremendous amount of redundant code. In particular, you replicate the operations for each of the four operators. Here is a more Ruby-like way of implementing your calculator.
Code
def evaluate(string)
arr = create_arr(string)
until arr.size == 1
i = arr.index(arr.find { |e| e.is_a? Symbol })
arr[i-2] = arr[i-2].send(arr[i], arr[i-1])
arr.delete_at(i)
arr.delete_at(i-1)
end
arr.first
end
def create_arr(string)
string.split(/\s+/).map { |e| e =~ /-?[0-9]+/ ? e.to_i : e.to_sym }
end
The line in create_arr could alternatively end : e } (sent accepts a string or symbol for the method), in which case e.is_a? Symbol would be changed to e.is_a? String.
Examples
evaluate("3 4 * 2 / 3 - 2 *") #=> 6
evaluate("10 2 / 3 + 2 / 2 -") #=> 2
evaluate("8 -2 / 1 +") #=> -3
evaluate("5 1 2 + 4 * + 3 -") #=> 14
Explanation
Suppose
string = "2 3 4 * 2 / +"
Step 1
arr = create_arr(string) #=> [2, 3, 4, :*, 2, :/, :+]
arr.size == 1 #=> false
v = arr.find { |e| e.is_a? Symbol } #=> :*
i = arr.index(v) #=> 3
arr[i-2] = arr[i-2].send(arr[i], arr[i-1])
# arr[1] = arr[1].send(arr[3], arr[2])
# arr[1] = 3.send(:*, 4) #=> 12
arr #=> [2, 12, 4, :*, 2, :/, :+]
arr.delete_at(i) #=> :*
arr #=> [2, 12, 4, 2, :/, :+]
arr.delete_at(i-1) #=> 4
arr #=> [2, 12, 2, :/, :+]
Step 2
arr.size == 1 #=> false
v = arr.find { |e| e.is_a? Symbol } #=> :/
i = arr.index(v) #=> 3
arr[i-2] = arr[i-2].send(arr[i], arr[i-1])
# arr[1] = arr[1].send(arr[3], arr[2])
# arr[1] = 12.send(:/, 2) #=> 6
arr #=> [2, 6, 2, :/, :+]
arr.delete_at(i) #=> :/
arr #=> [2, 6, 2, :+]
arr.delete_at(i-1) #=> 2
arr #=> [2, 6, :+]
Step 3
arr.size == 1 #=> false
v = arr.find { |e| e.is_a? Symbol } #=> :+
i = arr.index(v) #=> 2
arr[i-2] = arr[i-2].send(arr[i], arr[i-1])
# arr[0] = arr[0].send(arr[2], arr[1])
# arr[0] = 2.send(:+, 6) #=> 8
arr #=> [8, 6, :+]
arr.delete_at(i) #=> :+
arr #=> [8, 6]
arr.delete_at(i-1) #=> 6
arr #=> [8]
Step 4
arr.size == 1 #=> true
arr.first #=> 8
def peel array
output = []
while ! array.empty? do
output << array.shift
mutate! array
end
output.flatten
end
I have not included the mutate! method, because I am only interested in removing the output variable. The mutate! call is important because we cannot iterate over the array using each because array is changing.
EDIT: I am getting an array as output, which is what I want. The method works correctly, but I think there is a way to collect the array.shift values without using a temp variable.
EDIT #2: OK, here is the mutate! method and test case:
def mutate! array
array.reverse!
end
a = (1..5).to_a
peel( a ).should == [ 1, 5, 2, 4, 3 ]
It doesn't matter if peel modifies array. I guess it should be called peel!. Yes, mutate! must be called after each element is removed.
All this reversing makes me dizzy.
def peel(array)
indices = array.size.times.map do |i|
i = -i if i.odd?
i = i/2
end
array.values_at(*indices) # indices will be [0, -1, 1, -2, 2] in the example
end
a = (1..5).to_a
p peel(a) #=>[1, 5, 2, 4, 3]
Another approach:
def peel(array)
mid = array.size/2
array[0..mid]
.zip(array[mid..-1].reverse)
.flatten(1)
.take(array.size)
end
Usage:
peel [1,2,3,4,5,6]
#=> [1, 6, 2, 5, 3, 4]
peel [1,2,3,4,5]
#=> [1, 5, 2, 4, 3]
Here's a way using parallel assignment:
def peel array
n = array.size
n.times {|i| (n-2-2*i).times {|j| array[n-1-j], array[n-2-j] = array[n-2-j], array[n-1-j]}}
array
end
peel [1,2,3,4,5] # => [1,5,2,4,3]
peel [1,2,3,4,5,6] # => [1,6,2,5,3,4]
What I'm doing here is a series of pairwise exchanges. By way of example, for [1,2,3,4,5,6], the first 6-2=4 steps (6 being the size of the array) alter the array as follows:
[1,2,3,4,6,5]
[1,2,3,6,4,5]
[1,2,6,3,4,5]
[1,6,2,3,4,5]
The 1, 6 and the 2 are in now the right positions. We repeat these steps, but this time only 6-4=2 times, to move the 5 and 3 into the correct positions:
[1,6,2,3,5,4]
[1,6,2,5,3,4]
The 4 is pushed to the end, it's correct position, so we are finished.
Can I get the next value in an each loop?
(1..5).each do |i|
#store = i + (next value of i)
end
where the answer would be..
1 + 2 + 2 + 3 + 3 + 4 + 4 + 5 + 5 = 29
And also can I get the next of the next value?
From as early as Ruby 1.8.7, the Enumerable module has had a method each_cons that does almost exactly what you want:
each_cons(n) { ... } → nil
each_cons(n) → an_enumerator
Iterates the given block for each array of consecutive <n> elements. If no block is given, returns an enumerator.
e.g.:
(1..10).each_cons(3) { |a| p a }
# outputs below
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
The only problem is that it doesn't repeat the last element. But that's trivial to fix. Specifically, you want
store = 0
range = 1..5
range.each_cons(2) do |i, next_value_of_i|
store += i + next_value_of_i
end
store += range.end
p store # => 29
But you could also do this:
range = 1..5
result = range.each_cons(2).reduce(:+).reduce(:+) + range.end
p result # => 29
Alternatively, you may find the following to be more readable:
result = range.end + range.each_cons(2)
.reduce(:+)
.reduce(:+)
Like this:
range = 1..5
store = 0
range.each_with_index do |value, i|
next_value = range.to_a[i+1].nil? ? 0 : range.to_a[i+1]
store += value + next_value
end
p store # => 29
There may be better ways, but this works.
You can get the next of the next value like this:
range.to_a[i+2]
One approach that wouldn't use indexes is Enumerable#zip:
range = 11..15
store = 0 # This is horrible imperative programming
range.zip(range.to_a[1..-1], range.to_a[2..-1]) do |x, y, z|
# nil.to_i equals 0
store += [x, y, z].map(&:to_i).inject(:+)
end
store