What does this snippet of code in the children variable array mean? - ruby

def breadth_first_search(adj_matrix, source_index, end_index)
node_queue = [source_index]
puts "#{source_index} stored into queue \n"
loop do
curr_node = node_queue.pop
puts "#{curr_node} popped \n"
return false if curr_node == nil
return true if curr_node == end_index
children = (0..adj_matrix.length-1).to_a.select do |i|
adj_matrix[curr_node][i] == 1
end
puts "children: #{children}"
node_queue = children + node_queue
puts "node_queue: #{node_queue}"
end
end
I can understand what it does conceptually, but the actual syntax is what I don't understand. Is the children variable an array that stores elements with a loop?

The variable children is being set to the result of the enumerable method select
select is a method often called on an array which takes a block. The return value is only the values of the original array in which the block returns a truthy object (e.g. not false, not nil)
(0..adj_matrix.length-1) is a range object, from 0 to the length of adj_matrix, which is converted to an array via to_a (resulting in [0, 1, 2, ...])
Then that array is filtered by select to only be the values in which adj_matrix[curr_node][i] == 1. So if the nodes at index 0 and 2, are equal to 1
then the result would be [0, 2]

Related

Syncing a random variable in ruby

I am making a simple Tic tac toe game where the rows are represented by three arrays and columns are items in those arrays. For my computer choice I use two separate functions to get the row and column.
$first = ["-","-","-"]
$secound = ["-","-","-"]
$third = ["-","-","-"]
def get_row
row = nil
case rand(3)
when 0
row = $first
when 1
row = $secound
else
row = $third
end
row
end
def get_col
col = nil
case rand(3)
when 0
col = 0
when 1
col = 1
else
col = 2
end
col
end
I then use a third function that keeps generating a new "guess" until it finds an empty spot, at which point it marks an 0.
def computer_guess
temp = false
try = get_row[get_col]
while temp == false
if try == "-"
get_row[get_col] = "O"
temp = true
else
try = get_row[get_col]
end
end
end
My problem is that i cant return the guess to the same position that i check for validity. Is the there a way to sync these or do i need a different approach?
The problem with your approach is that get_row[get_col] returns a random element every time it is called, so likewise get_row[get_col] = "O" will set a random element to "O". You inspect one element and then set another.
You could fix this quickly by modifying the retrieved element in-place:
if try == "-"
try.replace("O")
# ...
But semantically, I don't like that fix very much. Thinking of a tic-tac-toe board, I'd rather assign an "O" to the free spot than transforming the existing placeholder from "-" into "O".
Did you notice that your get_row method returns an array whereas get_col returns an index? I think this mixture of arrays and indices makes your code a bit convoluted.
It's much easier (in my opinion) to access both, row and column via an index.
To do so, you could put your three rows into another array:
$board = [
['-', '-', '-'],
['-', '-', '-'],
['-', '-', '-']
]
The first row can be accessed via $board[0] and its first element via $board[0][0]. To set an element you'd use: $board[1][2] = 'O' (this sets the middle row's right-most element to "O"). Of course, you can also use variables, e.g. $board[row][col].
With this two-dimensional array, your computer_guess could be rewritten using just two random indices: (get_row and get_col aren't needed anymore)
def computer_guess
loop do
row = rand(3) # random row index
col = rand(3) # random column index
if $board[row][col] == '-' # if corresponding spot is "-"
$board[row][col] = 'O' # set that spot to "O"
break # and break out of the loop
end
end
end
Note however that "blindly" guessing spots until you find a free one might not be the best approach.
Instead, you could generate a list of "free" spots. This can be done by first generating an array of all coordinates1 and then select-ing those row/col pairs whose spot is "-":
def free_spots
coordinates = [0, 1, 2].product([0, 1, 2])
coordinates.select { |row, col| $board[row][col] == '-' }
end
Now you just have to chose a random pair (via sample) and set the corresponding spot to "O":
def computer_guess
row, col = free_spots.sample
$board[row][col] = 'O'
end
1 Array#product returns the Cartesian product of the given arrays. It's an easy way to get all pairs:
[0, 1, 2].product([0, 1, 2])
#=> [[0, 0], [0, 1], [0, 2],
# [1, 0], [1, 1], [1, 2],
# [2, 0], [2, 1], [2, 2]]
Here is another approach. This code imitates game computer against itself. You can easily replace one computer's move with user input.
ROWS = 3
COLUMNS = 3
MAX_TURNS = ROWS * COLUMNS
def random_cell
{ row: rand(ROWS), column: rand(COLUMNS) }
end
def empty_random_cell(board)
while true
cell = random_cell
return cell if board_value(board, cell).nil?
end
end
def board_value(board, cell)
board[cell[:row]][cell[:column]]
end
def turn(board, cell, value)
row = cell[:row]
column = cell[:column]
board[row][column] = value
end
def print_board(board)
ROWS.times do |row|
COLUMNS.times do |column|
value = board[row][column]
print value.nil? ? '_' : value
print ' '
end
puts
end
end
board = Array.new(ROWS) { Array.new(COLUMNS) }
print_board(board)
turns_counter = 0
while true
cell = empty_random_cell(board)
turn(board, cell, 'X')
turns_counter += 1
break if turns_counter == MAX_TURNS
cell = empty_random_cell(board)
turn(board, cell, '0')
turns_counter += 1
break if turns_counter == MAX_TURNS
end
print_board(board)
when there is only one available spot, it might take time to guess it.
So it might be better to collate the available spots and get a random value from there:
def computer_guess
free_spots=[]
free_spots.concat($first.each_with_index.map{|x,i|x=='-' ? ['$first',i] : nil}.compact)
free_spots.concat($secound.each_with_index.map{|x,i|x=='-' ? ['$secound',i] : nil}.compact)
free_spots.concat($third.each_with_index.map{|x,i|x=='-' ? ['$third',i] : nil}.compact)
try=free_spots.sample
eval"#{try[0]}[#{try[1]}]='O'" unless try.nil?
end

How can I store my outcome into an array in ruby language?

My teacher asked me to write a Ruby program that contain a loop and in each iteration it randomly makes a choice: head (represented by 1) or tail (represented by 0), and stores the outcome (0 or 1) in an array. I am not sure how to store my outcome into an array and this is my code
p "Select a number between 50 to 100"
x = gets.to_i
i = 0
array[x]
while i < x
a = rand(0..1)
array.push(a)
i += 1
end
Hay, may you can try the following:
number_of_times = gets.to_i
array = []
number_of_times.times do
random_result = rand(0..1)
array.push(random_result)
end
or (like the comments)
n = gets.to_i
array = n.times.map{ rand(0..1) }
After executing this, array will have the result of all randomized options.
Note1: You don't need to initialize array with the width, you can just initialize the array and push elements to him.
Note2: No problems with your while operator, but in ruby, you have the .times iterator, is more legible for this case.
You can leverage Ruby's Array block constructor to allocate and initialize your array in a one-liner:
array = Array.new(n) { rand(0..1) } # sample result: [0, 0, 1, 0, 1, 0] for n = 6
The argument n determines the size of the array, and the block argument is used to initialize each of the n elements.

Why do I get a 'typeerror' when using inject in Ruby?

I am using this inject method to make a running total of values into an array. I am trying to figure out why I am getting an error.
def running_totals(myarray)
results = []
myarray.inject([]) do |sum,n|
results << sum + n
end
results
end
p running_totals([1,2,3,4,5])
I am getting the error
in `+': no implicit conversion of Fixnum into Array (TypeError)
When breaking this method down, isn't this the same as adding two integers and adding that into an array? I'm a bit confused here. Thanks for the help.
In the first iteration sum will be an array (as you specified an array as the default when calling inject([])) and you try to add a number to it. in the results << sum + n statement
Instead, set the initial value to 0, then add, then add the result to the array, then make sure you let sum get passed into the next iteration of inject.
def running_totals(myarray)
results = []
myarray.inject(0) do |sum,n| # First iteration sum will be 0.
sum += n # Add value to sum.
results << sum # Push to the result array.
sum # Make sure sum is passed to next iteration.
end
results
end
p running_totals([1,2,3,4,5]) #=> [1, 3, 6, 10, 15]
The result of results << sum + n is an array results and it's this that's replacing the sum value and so the next iteration you're trying to add a fixnum n into an array sum ... plus it doesn't help that you're initializing the value of sum to be an array.
Make sure that the last executed statement in your inject block is what you want the accumulated value to be.
def running_totals(myarray)
results = []
results << myarray.inject do |sum, n|
results << sum
sum + n
end
results
end
p running_totals([1,2,3,4,5])
=> [1, 3, 6, 10, 15]
Note that I moved the result of the inject into results array as well, so that the final value is also included, otherwise you'd only have the four values and would be missing the final (15) value.
The return value of the inject block is passed as the first argument the next time the block is called, so those have to match. In your code, you're passing an array as an intital value, and then returning an array; so far, so good. But inside the code block you treat that array parameter (sum) as a number, which won't work. Try this:
def running_totals(myarray)
myarray.inject([]) do |results,n|
results << n + (results.last || 0)
end
end
The [] passed as an argument to inject becomes the first value of results; the first array element (1 in your example) becomes the first value of n. Since results is empty, results.last is nil and the result of (results.last || 0) is 0, which we add to n to get 1, which we push onto results and then return that newly-modified array value from the block.
The second time into the block, results is the array we just returned from the first pass, [1], and n is 2. This time results.last is 1 instead of nil, so we add 1 to 2 to get 3 and push that onto the array, returning [1,3].
The third time into the block, results is [1,3], and n is 3, so it returns [1,3,6]. And so on.
According to ri, you have to return result of the computation from inject's block.
From: enum.c (C Method):
Owner: Enumerable
Visibility: public
Signature: inject(*arg1)
Number of lines: 31
Combines all elements of enum by applying a binary
operation, specified by a block or a symbol that names a
method or operator.
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.
If you do not explicitly specify an initial value for memo,
then uses the first element of collection is used as the initial value
of memo.
Examples:
# Sum some numbers
(5..10).reduce(:+) #=> 45
# Same using a block and inject
(5..10).inject {|sum, n| sum + n } #=> 45
# Multiply some numbers
(5..10).reduce(1, :*) #=> 151200
# Same using a block
(5..10).inject(1) {|product, n| product * n } #=> 151200
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo,word|
memo.length > word.length ? memo : word
end
longest
So your sample would work if you return computation result for each iteration, something like this:
def running_totals(myarray)
results = []
myarray.inject do |sum,n|
results << sum + n
results.last # return computation result back to Array's inject
end
results
end
Hope it helps.

recursive bubble sort ruby

I am trying to build a recursive bubble sorting method in Ruby. It makes it through one pass fine, but it keeps returning after that. It seems that my
if array == swapped_array is being triggered, but I don't understand where my array variable is being redefined.
def bubble_sort(array, swapped = true)
return array if swapped == false
i = 0
if swapped == true
swapped_array = comparator(array)
end
if array == swapped_array
swapped = false
bubble_sort(array, swapped)
else bubble_sort(swapped_array)
end
end
def comparator(array, i = 0)
return array if i == array.length - 1
if array[i] > array[i+1]
array[i], array[i+1] = array[i+1], array[i]
end
i += 1
comparator(array, i)
end
swapped_array = comparator(array)
... takes the return value of comparator and assigns it to swapped_array. But comparator (eventually) returns the original argument array, which is the same array as the one defined in the caller. So
array == swapped_array
is always true.
If you want to compare two different arrays, you can call .dup on the array. This creates a new object with the same values as the original.
It is because in comparator, you are swapping the elements in the original array. Whatever you do to it, array and swapped_array in bubble_sort refer to the same array instance, and hence will always be the same. To solve the problem, add a single line to the top of your definition:
def comparator(array, i = 0)
array = array.dup
...
end

Merge sort algorithm using recursion

I'm doing The Odin Project. The practice problem is: create a merge sort algorithm using recursion. The following is modified from someone's solution:
def merge_sort(arry)
# kick out the odds or kick out of the recursive splitting?
# I wasn't able to get the recombination to work within the same method.
return arry if arry.length == 1
arry1 = merge_sort(arry[0...arry.length/2])
arry2 = merge_sort(arry[arry.length/2..-1])
f_arry = []
index1 = 0 # placekeeper for iterating through arry1
index2 = 0 # placekeeper for iterating through arry2
# stops when f_arry is as long as combined subarrays
while f_arry.length < (arry1.length + arry2.length)
if index1 == arry1.length
# pushes remainder of arry2 to f_arry
# not sure why it needs to be flatten(ed)!
(f_arry << arry2[index2..-1]).flatten!
elsif index2 == arry2.length
(f_arry << arry1[index1..-1]).flatten!
elsif arry1[index1] <= arry2[index2]
f_arry << arry1[index1]
index1 += 1
else
f_arry << arry2 [index2]
index2 += 1
end
end
return f_arry
end
Is the first line return arry if arry.length == 1 kicking it out of the recursive splitting of the array(s) and then bypassing the recursive splitting part of the method to go back to the recombination section? It seems like it should then just keep resplitting it once it gets back to that section as it recurses through.
Why must it be flatten-ed?
The easiest way to understand the first line is to understand that the only contract that merge_sort is bound to is to "return a sorted array" - if the array has only one element (arry.length == 1) it is already sorted - so nothing needs to be done! Simply return the array itself.
In recursion, this is known as a "Stop condition". If you don't provide a stop condition - the recursion will never end (since it will always call itself - and never return)!
The result you need to flatten your result, is because you are pushing an array as an element in you resulting array:
arr = [1]
arr << [2, 3]
# => [1, [2, 3]]
If you try to flatten the resulting array only at the end of the iteration, and not as you are adding the elements, you'll have a problem, since its length will be skewed:
arr = [1, [2, 3]]
arr.length
# => 2
Although arr contains three numbers it has only two elements - and that will break your solution.
You want all the elements in your array to be numbers, not arrays. flatten! makes sure that all elements in your array are atoms, and if they are not, it adds the child array's elements to itself instead of the child array:
arr.flatten!
# => [1, 2, 3]
Another you option you might want to consider (and will be more efficient) is to use concat instead:
arr = [1]
arr.concat([2, 3])
# => [1, 2, 3]
This method add all the elements in the array passed as parameter to the array it is called on.

Resources