I have this code:
def test(vertices, distances)
until vertices.empty?
nearest_vertex = vertices.inject do |a, b|
p "a = #{a}: b = #{b}"
p "distances[a] = #{distances[a]}, distances[b] = #{distances[b]}"
next b unless distances[a] #next b if distances[a] == true
next a unless distances[b] #next a if distances[b] == true
next a if distances[a] < distances[b]
p "b = #{b}"
b
end
p "nearest_vertex = #{nearest_vertex}"
vertices.delete nearest_vertex
end
end
vertices = [1, 2, 3, 4, 5, 6]
distances = {1 => 0, 2 => 3, 3 => 2, 4 => 18, 5 => nil, 6 => 7}
test(vertices, distances)
Which outputs:
"a = 1: b = 2"
"distances[a] = 0, distances[b] = 3"
"a = 1: b = 3"
"distances[a] = 0, distances[b] = 2"
...
"a = 1: b = 6"
"distances[a] = 0, distances[b] = 7"
"nearest_vertex = 1"
Here, b = 6 isn't printed. Is this because next issues a stop iteration command?
"a = 2: b = 3"
"distances[a] = 3, distances[b] = 2"
"b = 3"
Why doesn't the iteration continue to a=2: b=4 here?
"a = 3: b = 4"
"distances[a] = 2, distances[b] = 18"
"a = 3: b = 5"
"distances[a] = 2, distances[b] = "
"a = 3: b = 6"
"distances[a] = 2, distances[b] = 7"
"nearest_vertex = 3"
...
Once a is set to 3, everything works as I thought it should. How does the program know that nearest_vertex is three?
I don't understand the interaction between inject and next in determining how and when to declare a vertex to be the nearest_vertex. How are the distances compared when there is no comparison operator?
Let me explain Enumerable#inject in pure Ruby. Note that the following code is NOT the original implementation of Enumerable#inject. For clarity, I will explain it in class Array, and focus on the most basic usage ary.inject(&block):
class Array
def inject(&block)
return nil if self.empty?
enumerator = self.each
accumulator = enumerator.next
loop do
begin
accumulator = block.call(accumulator, enumerator.next)
rescue StopIteration
break
end
end
accumulator
end
end
You can see that in the loop, the accumulator of previous iteration and the current element of the array is passed to the block's params, and the block's return value is reassigned to the accumulator.
Then what is next x in a block?
You can think of a block as an anonymous function, and the keyword next is its return. It terminates the current block call and makes the block return x (nil if the return value is not explicitly specified).
By the way, break x in a block terminates the method call which takes the block, and makes the method return x. For example:
[1, 2, 3, 4].inject do |acc, n|
break n if n == 2
acc + n
end
=> 2
The Array#inject is terminated by the break when n is 2, and that n is returned.
return in a block terminates the method call which calls the method that takes the block. For example:
def foo
[1, 2, 3].inject do |acc, n|
return n
end
puts 'You will never see this this sentence.'
end
foo
=> 2
And there is no sentence printed, because foo is terminated by return.
How inject works
The block passed to inject receives two arguments in each iteration. The first argument (prev_nearest_key here) is an "accumulator" whose value is whatever value was returned by the previous iteration. (For the first iteration it will be the argument given to inject or, inits absence, the first element of the collection—vertices[0] here.) The second argument (key) is the current element of the collection. When iteration is complete, the final value of the accumulator is returned.
When you call next val in a block passed to an iterator, val is immediately returned from the block and the next iteration begins. To demonstrate, here's how it looks with map:
["h", "e", "l", "l", "o"].map do |letter|
next letter.upcase if "aeoiu".include?(letter)
letter
end
# => ["h", "E", "l", "l", "O"]
Above, when letter is a vowel, letter.upcase is returned from the block and the next line is never evaluated. When letter isn't a vowel, it's returned from the block unchanged.
Here's a similar example with inject:
["h", "e", "l", "l", "o"].inject do |accum, letter|
next accum + letter.upcase if "aeoiu".include?(letter)
accum + letter
end
# => "hEllO"
What's different here? When letter is a vowel, accum + letter.upcase is returned from the block (effectively appending letter.upcase to accum), and the next line is never evaluated. When letter isn't a vowel, accum + letter is returned from the block. In both cases, the value returned from the block becomes accum in the next iteration.
How your code works
Here's your code, but with more intelligible variable names.
nearest_vertex = vertices.inject do |prev_nearest_vertex, current_vertex|
next current_vertex unless distances[prev_nearest_vertex]
next prev_nearest_vertex unless distances[current_vertex]
next prev_nearest_vertex if distances[prev_nearest_vertex] < distances[current_vertex]
current_vertex
end
I've renamed a, the accumulator, to prev_nearest_vertex, since it's the value returned by the previous iteration, and b to current_vertex.
The first two lines inside the block are just checking to see if distances[prev_nearest_vertex] and distances[current_vertex] are nil. They could (and, perhaps, should) be written like this instead:
next current_vertex if distances[prev_nearest_vertex].nil?
next prev_nearest_vertex if distances[current_vertex].nil?
The first line basically says, "If the previous nearest vertex's distance is nil, then it's not the nearest, so set prev_nearest_vertex to current_vertex in the next iteration." The second line says "If the current vertex's distance is nil, then it's not the nearest, so keep prev_nearest_vertex the same in the next iteration.
And here are the third and fourth lines:
next prev_nearest_vertex if distances[prev_nearest_vertex] < distances[current_vertex]
current_vertex
These could be rewritten like this:
if distances[prev_nearest_vertex] < distances[current_vertex]
prev_nearest_vertex
else
current_vertex
end
It just says, "Set prev_nearest_vertex in the next iteration to prev_nearest_vertex if its distance is less; otherwise set it to current_vertex.
This is pretty good code, but you should probably...
Do this instead
Ruby's Enumerable module has a lot of really useful methods, including one called min_by. It evaluates the given block for each element in an Enumerable and returns the element for which the lowest value was returned. To demonstrate, consider this map:
words = ["lorem", "ipsum", "dolor", "sit", "amet"]
words.map {|word| word.size }
# => [5, 5, 5, 3, 4]
This just turns an array of words into an array of their sizes. Now suppose we want to get the word that's the shortest. This is easy with min_by:
words = ["lorem", "ipsum", "dolor", "sit", "amet"]
words.min_by {|word| word.size }
# => "sit"
Instead of returning the words' sizes, this calculates their sizes and then returns the word whose size is the smallest.
This is directly applicable to your code. Again, consider a map operation:
vertices = [1, 2, 3, 4, 5, 6]
distances = { 1 => 0, 2 => 3, 3 => 2, 4 => 18, 5 => nil, 6 => 7 }
vertices.map do |vertex|
distances[vertex] || Float::INFINITY
end
# => [0, 3, 2, 18, Infinity, 7]
This produces an array of distances corresponding to the elements in vertices, but nil distances are replaced with Float::INFINITY. This is useful because n < Float::INFINITY is true for every number n. As before, we can now replace map with min_by to get the vertex corresponding to the smallest distance:
def test(vertices, distances)
vertices.min_by {|vertex| distances[vertex] || Float::INFINITY }
end
test(vertices, distances)
# => 1
Related
I am asked to write some code in Ruby that iterates over every n-th element of an array and prints it until all elements of the array are printed.
The question reads:
Imagine an iterator that accesses an array in strides and runs some code at each stride. If the strides reach the end of the array then they simply begin anew from the array's beginning.
For example:
x = [0,1,2,3,4]
x.stride(1) do |elem|; puts elem; end # prints 0,1,2,3,4
x.stride(2) do |elem|; puts elem; end # prints 0,2,4,1,3
x.stride(8) do |elem|; puts elem; end # prints 0,3,1,4,2
[].stride(2) do |elem|; puts elem; end # does not print anything, but the code is correct
Assume that the stride is equal or greater than 1, and that both the stride and the array's size are not a integral/whole multiple of each other, meaning that the whole array can be printed using a given stride. Fill in the code that's missing:
class Array
def stride(step)
numelems = ... # size of the array
...
end
end
It is obvious that numelemns = self.length(). However am having trouble with the rest.
I am going to try writing some code in Python that accomplishes this task, but I am afraid that I will not be able to translate it to Ruby.
Any ideas? The answer should not be more than 4-5 lines long as the question is one that our proffessor gave us to solve in a couple of minutes.
A solution to this is provided below (thanks #user3574603):
class Array
def stride(step)
yield self[0]
(self * step).map.with_index do |element, index|
next element if index == 0
yield element if index % step == 0
end
end
end
The following assumes that arr.size and n are not both even numbers and arr.size is not a multiple of n.
def striding(arr, n)
sz = arr.size
result = '_' * sz
idx = 0
sz.times do
result[idx] = arr[idx].to_s
puts "S".rjust(idx+1)
puts result
idx = (idx + n) % sz
end
end
striding [1,2,3,4,5,6,7,8,9,1,2,3,4,5,6], 7
S
1______________
S
1______8_______
S
1______8______6
S
1_____78______6
S
1_____78_____56
S
1____678_____56
S
1____678____456
S
1___5678____456
S
1___5678___3456
S
1__45678___3456
S
1__45678__23456
S
1_345678__23456
S
1_345678_123456
S
12345678_123456
S
123456789123456
Here is an example where arr.size is a multiple of n.
striding [1,2,3,4,5,6], 3
S
1_____
S
1__4__
S
1__4__
S
1__4__
S
1__4__
S
1__4__
Here is an example where arr.size and n are both even numbers.
striding [1,2,3,4,5,6,7,8], 6
S
1_______
S
1_____7_
S
1___5_7_
S
1_3_5_7_
S
1_3_5_7_
S
1_3_5_7_
S
1_3_5_7_
S
1_3_5_7_
Imagine an iterator that accesses an array in strides and runs some code at each stride. If the strides reach the end of the array then they simply begin anew from the array's beginning.
Based on this specification, stride will always iterate forever, unless the array is empty. But that is not a problem, since we can easily take only the amount of elements we need.
In fact, that is a good design: producing an infinite stream of values lets the consumer decide how many they need.
A simple solution could look like this:
module CoreExtensions
module EnumerableExtensions
module EnumerableWithStride
def stride(step = 1)
return enum_for(__callee__, step) unless block_given?
enum = cycle
loop do
yield(enum.next)
(step - 1).times { enum.next }
end
self
end
end
end
end
Enumerable.include(CoreExtensions::EnumerableExtensions::EnumerableWithStride)
A couple of things to note here:
I chose to add the stride method to Enumerable instead of Array. Enumerable is Ruby's work horse for iteration and there is nothing in the stride method that requires self to be an Array. Enumerable is simply the better place for it.
Instead of directly monkey-patching Enumerable, I put the method in a separate module. That makes it easier to debug code for others. If they see a stride method they don't recognize, and inspect the inheritance chain of the object, they will immediately see a module named EnumerableWithStride in the inheritance chain and can make the reasonable assumption that the method is probably coming from here:
[].stride
# Huh, what is this `stride` method? I have never seen it before.
# And it is not documented on https://ruby-doc.org/
# Let's investigate:
[].class.ancestors
#=> [
# Array,
# Enumerable,
# CoreExtensions::EnumerableExtensions::EnumerableWithStride,
# Object,
# Kernel,
# BasicObject
# ]
# So, we're confused about a method named `stride` and we
# found a module whose name includes `Stride`.
# We can reasonably guess that somewhere in the system,
# there must be a file named
# `core_extensions/enumerable_extensions/enumerable_with_stride.rb`.
# Or, we could ask the method directly:
meth = [].method(:stride)
meth.owner
#=> CoreExtensions::EnumerableExtensions::EnumerableWithStride
meth.source_location
#=> [
# 'core_extensions/enumerable_extensions/enumerable_with_stride.rb',
# 6
# ]
For an empty array, nothing happens:
[].stride(2, &method(:p))
#=> []
stride just returns self (just like each does) and the block is never executed.
For a non-empty array, we get an infinite stream of values:
x.stride(&method(:p))
# 0
# 1
# 2
# 3
# 4
# 0
# 1
# …
x.stride(2, &method(:p))
# 0
# 2
# 4
# 1
# 3
# 0
# 2
# …
x.stride(8, &method(:p))
# 0
# 3
# 1
# 4
# 2
# 0
# 3
# …
The nice thing about this infinite stream of values is that we, as the consumer can freely choose how many elements we want. For example, if I want 10 elements, I simply take 10 elements:
x.stride(3).take(10)
#=> [0, 3, 1, 4, 2, 0, 3, 1, 4, 2]
This works because, like all well-behaved iterators, our stride method returns an Enumerator in case no block is supplied:
enum = x.stride(2)
#=> #<Enumerator: ...>
enum.next
#=> 0
enum.next
#=> 2
enum.next
#=> 4
enum.next
#=> 1
enum.next
#=> 3
enum.next
#=> 0
enum.next
#=> 2
So, if we want to implement the requirement "until all the elements of the array are printed":
I am asked to write some code in Ruby that iterates over every n-th element of an array and prints it until all elements of the array are printed.
We could implement that something like this:
x.stride.take(x.length).each(&method(:p))
x.stride(2).take(x.length).each(&method(:p))
x.stride(8).take(x.length).each(&method(:p))
This is a pretty simplistic implementation, though. Here, we simply print as many elements as there are elements in the original array.
We could implement a more sophisticated logic using Enumerable#take_while that keeps track of which elements have been printed and which haven't, and only stops if all elements are printed. But we can easily prove that after x.length iterations either all elements have been printed or there will never be all elements printed (if the stride size is an integral multiple of the array length or vice versa). So, this should be fine.
This almost does what I think you want but breaks if the step is array.length + 1 array.length (but you mention that we should assume the stride is not a multiply of the array length).
class Array
def exhaustive_stride(step)
(self * step).map.with_index do |element, index|
next element if index == 0
element if index % step == 0
end.compact
end
end
x.exhaustive_stride 1
#=> [0, 1, 2, 3, 4]
x.exhaustive_stride 2
#=> [0, 2, 4, 1, 3]
x.exhaustive_stride 8
#=> [0, 3, 1, 4, 2]
[].exhaustive_stride 2
#=> []
Using the example array, it breaks when the stride is 5.
[0,1,2,3,4].exhaustive_stride 5
#=> [0, 0, 0, 0, 0]
Note
This works but the intermediate array makes it highly inefficient. Consider other answers.
Here's another solution that uses recursion. Not the most efficient but one way of doing it.
class Array
def exhaustive_stride(x, r = [])
return [] if self.empty?
r << self[0] if r.empty?
while x > self.length
x -= self.length
end
r << self[x]
x += x
return r if r.count == self.count
stride(x, r)
end
end
[0,1,2,3,4].exhaustive_stride 1
#=> [0, 1, 2, 4, 3]
[0,1,2,3,4].exhaustive_stride 2
#=> [0, 2, 4, 3, 1]
[0,1,2,3,4].exhaustive_stride 8
#=> [0, 3, 1, 2, 4]
[].exhaustive_stride 2
#=> []
[0,1,2,3,4].exhaustive_stride 100_000_001
#=> [0, 1, 2, 4, 3]
This would work:
def stride(ary, step)
raise ArgumentError unless step.gcd(ary.size) == 1
Array.new(ary.size) { |i| ary[(i * step) % ary.size] }
end
Example:
x = [0, 1, 2, 3, 4]
stride(x, 1) #=> [0, 1, 2, 3, 4]
stride(x, 2) #=> [0, 2, 4, 1, 3]
stride(x, 8) #=> [0, 3, 1, 4, 2]
stride(x, -1) #=> [0, 4, 3, 2, 1]
First of all, the guard clause checks whether step and ary.size are coprime to ensure that all elements can be visited via step.
Array.new(ary.size) creates a new array of the same size as the original array. The elements are then retrieved from the original array by multiplying the element's index by step and then performing a modulo operation using the array's size.
Having % arr.size is equivalent to fetching the elements from a cyclic array, e.g. for a step value of 2:
0 1 2 3 4
| | | | |
[0, 1, 2, 3, 4, 0, 1, 2, 3, 4, ...
To turn this into an instance method for Array you merely replace ary with self (which can be omitted most of the time):
class Array
def stride(step)
raise ArgumentError unless step.gcd(size) == 1
Array.new(size) { |i| self[(i * step) % size] }
end
end
I'm working on a lab Using a generalized map method to pass an element and block through returning multiple outcomes.
Really struggled on this one. Found some responses but they don't really make sense to me.
Here is the code:
def map(s)
new = []
i = 0
while i < s.length
new.push(yield(s[i]))
i += 1
end
new
end
Here's is the test:
it "returns an array with all values made negative" do
expect(map([1, 2, 3, -9]){|n| n * -1}).to eq([-1, -2, -3, 9])
end
it "returns an array with the original values" do
dune = ["paul", "gurney", "vladimir", "jessica", "chani"]
expect(map(dune){|n| n}).to eq(dune)
end
it "returns an array with the original values multiplied by 2" do
expect(map([1, 2, 3, -9]){|n| n * 2}).to eq([2, 4, 6, -18])
end
it "returns an array with the original values squared" do
expect(map([1, 2, 3, -9]){|n| n * n}).to eq([1, 4, 9, 81])
end
end
I don't get how the above code can give you these 4 different results.
Could someone help me understand it ?
Thank you for your help!
How your method map works
To see how your method operates let's modify your code to add some intermediate variables and some puts statements to show the values of those variables.
def map(s)
new = []
i = 0
n = s.length
puts "s has length #{n}"
while i < n
puts "i = #{i}"
e = s[i]
puts " Yield #{e} to the block"
rv = yield(e)
puts " The block's return value is #{rv}. Push #{rv} onto new"
new.push(rv)
puts " new now equals #{new}"
i += 1
end
puts "We now return the value of new"
new
end
Now let's execute the method with one of the blocks of interest.
s = [1, 2, 3, -9]
map(s) { |n| n * 2 }
#=> [2, 4, 6, -18] (return value of method)
The following is displayed.
s has length 4
i = 0
Yield 1 to the block
The block's return value is 2. Push 2 onto new
new now equals [2]
i = 1
Yield 2 to the block
The block's return value is 4. Push 4 onto new
new now equals [2, 4]
i = 2
Yield 3 to the block
The block's return value is 6. Push 6 onto new
new now equals [2, 4, 6]
i = 3
Yield -9 to the block
The block's return value is -18. Push -18 onto new
new now equals [2, 4, 6, -18]
We now return the value of new
It may by of interest to execute this modified method with different values of s and different blocks.
A replacement for Array#map?
Is this a replacement for Array#map (or Enumerable#map, but for now let's just consider Array#map)? As you defined it at the top level your map is an instance method of the class Object:
Object.instance_methods.include?(:map) #=> true
It must be invoked map([1,2,3]) { |n| ... } whereas Array#map is invoked [1,2,3].map { |n| ... }. Therefore, for your method map to be a replacement for Array#map you need to define it as follows.
class Array
def map
new = []
i = 0
while i < length
new.push(yield(self[i]))
i += 1
end
new
end
end
[1, 2, 3, -9].map { |n| n * 2 }
#=> [2, 4, 6, -18]
Simplify
We can simplify this method as follows.
class Array
def map
new = []
each { |e| new << yield(e) }
new
end
end
[1, 2, 3, -9].map { |n| n * 2 }
#=> [2, 4, 6, -18]
or, better:
class Array
def map
each_with_object([]) { |e,new| new << yield(e) }
end
end
See Enumerable#each_with_object.
Note that while i < length is equivalent to while i < self.length, because self., if omitted, is implicit, and therefore redundant. Similarly, each { |e| new << yield(e) } is equivalent to self.each { |e| new << yield(e) } and each_with_object([]) { ... } is equivalent to self.each_with_object([]) { ... }.
Are we finished?
If we examine the doc Array#map carefully we see that there are two forms of the method. The first is when map takes a block. Our method Array#map mimics that behaviour and that is the only behaviour needed to satisfy the given rspec tests.
There is a second form, however, where map is not given a block, in which case it returns an enumerator. That allows us to chain the method to another. For example (with Ruby's Array#map),
['cat', 'dog', 'pig'].map.with_index do |animal, i|
i.even? ? animal.upcase : animal
end
#=> ["CAT", "dog", "PIG"]
We could modify our Array#map to incorporate this second behaviour as follows.
class Array
def map
if block_given?
each_with_object([]) { |e,new| new << yield(e) }
else
to_enum(:map)
end
end
end
[1, 2, 3, -9].map { |n| n * 2 }
#=> [2, 4, 6, -18]
['cat', 'dog', 'pig'].map.with_index do |animal, i|
i.even? ? animal.upcase : animal
end
#=> ["CAT", "dog", "PIG"]
See Kernel#block_given? and Object#to_enum.
Notes
You might use, say, arr, rather than s as the variable holding the array, as s often denotes a string, just as h typically denotes a hash. One generally avoids names for variables and custom methods that are the names of core Ruby methods. That is also an objection to your use of new as a variable name, as there are many core methods named new.
I want to create a "bubble sort" method, which means that I take two consecutive elements in an array, compare them and if the left element is greater than the right element, they should switch the position. I want to repeat it until my array is sorted in ascending order.
My code only works partially. If my array is too big nothing will happen (I have to quit ruby with CTRL + C). With arrays smaller than 5 elements my code works fine:
def bubbles(array)
while array.each_cons(2).any? { |a, b| (a <=> b) >= 0 }
# "true" if there are two consecutives elements where the first one
# is greater than the second one. I know the error must be here somehow.
array.each_with_index.map do | number, index |
if array[index + 1].nil?
number
break
elsif number > array[index + 1]
array[index], array[index + 1] = array[index + 1], array[index] # Swap position!
else
number
end
end
end
p array
end
If I call my method with an array with 4 elements, it works fine:
bubbles([1, 5, 8, 3]) # => [1, 3, 5, 8]
If I call it with a bigger array, it doesn't work:
bubbles([5, 12, 2, 512, 999, 1, 2, 323, 2, 12]) # => Nothing happens. I have to quit ruby with ctrl + c.
Have I somehow created an infinite loop with my while statement?
The problem is in your stop condition. You won't stop until you have an array where each element is lesser than the next. But in your long array you have duplicated elements, so the sorted elements will have adjacent elements that are equal to each other.
Not being too fancy with your code will make your life easier :)
while array.each_cons(2).any? { |a, b| a > b }
I suggest you determine if the array is ordered in a separate method (and don't print the array from within the method:
def bubbles(array)
until ordered?(array)
...
end
array
end
Here's one way (among many) to define ordered?:
def ordered?(array)
enum = array.to_enum
loop do
return false if enum.next > enum.peek
end
true
end
ordered? [1,2,3,4,5] #=> true
ordered? [1,2,4,3,4] #=> false
Also, your code mutates the argument it receives (array), which is probably undesirable. You can avoid that by working on a copy, array.dup.
DESCRIPTION:
The purpose of my code is to take in input of a sequence of R's and C's and to simply store each number that comes after the character in its proper array.
For Example: "The input format is as follows: R1C4R2C5
Column Array: [ 4, 5 ] Row Array: [1,2]
My problem is I am getting the output like this:
[" ", 1]
[" ", 4]
[" ", 2]
[" ", 5]
**How do i get all the Row integers following R in one array, and all the Column integers following C in another seperate array. I do not want to create multiple arrays, Rather just two.
Help!
CODE:
puts 'Please input: '
input = gets.chomp
word2 = input.scan(/.{1,2}/)
col = []
row = []
word2.each {|a| col.push(a.split(/C/)) if a.include? 'C' }
word2.each {|a| row.push(a.split(/R/)) if a.include? 'R' }
col.each do |num|
puts num.inspect
end
row.each do |num|
puts num.inspect
end
x = "R1C4R2C5"
col = []
row = []
x.chars.each_slice(2) { |u| u[0] == "R" ? row << u[1] : col << u[1] }
p col
p row
The main problem with your code is that you replicate operations for rows and columns. You want to write "DRY" code, which stands for "don't repeat yourself".
Starting with your code as the model, you can DRY it out by writing a method like this to extract the information you want from the input string, and invoke it once for rows and once for columns:
def doit(s, c)
...
end
Here s is the input string and c is the string "R" or "C". Within the method you want
to extract substrings that begin with the value of c and are followed by digits. Your decision to use String#scan was a good one, but you need a different regex:
def doit(s, c)
s.scan(/#{c}\d+/)
end
I'll explain the regex, but let's first try the method. Suppose the string is:
s = "R1C4R2C5"
Then
rows = doit(s, "R") #=> ["R1", "R2"]
cols = doit(s, "C") #=> ["C4", "C5"]
This is not quite what you want, but easily fixed. First, though, the regex. The regex first looks for a character #{c}. #{c} transforms the value of the variable c to a literal character, which in this case will be "R" or "C". \d+ means the character #{c} must be followed by one or more digits 0-9, as many as are present before the next non-digit (here a "R" or "C") or the end of the string.
Now let's fix the method:
def doit(s, c)
a = s.scan(/#{c}\d+/)
b = a.map {|str| str[1..-1]}
b.map(&:to_i)
end
rows = doit(s, "R") #=> [1, 2]
cols = doit(s, "C") #=> [4, 5]
Success! As before, a => ["R1", "R2"] if c => "R" and a =>["C4", "C5"] if c => "C". a.map {|str| str[1..-1]} maps each element of a into a string comprised of all characters but the first (e.g., "R12"[1..-1] => "12"), so we have b => ["1", "2"] or b =>["4", "5"]. We then apply map once again to convert those strings to their Fixnum equivalents. The expression b.map(&:to_i) is shorthand for
b.map {|str| str.to_i}
The last computed quantity is returned by the method, so if it is what you want, as it is here, there is no need for a return statement at the end.
This can be simplified, however, in a couple of ways. Firstly, we can combine the last two statements by dropping the last one and changing the one above to:
a.map {|str| str[1..-1].to_i}
which also gets rid of the local variable b. The second improvement is to "chain" the two remaining statements, which also rids us of the other temporary variable:
def doit(s, c)
s.scan(/#{c}\d+/).map { |str| str[1..-1].to_i }
end
This is typical Ruby code.
Notice that by doing it this way, there is no requirement for row and column references in the string to alternate, and the numeric values can have arbitrary numbers of digits.
Here's another way to do the same thing, that some may see as being more Ruby-like:
s.scan(/[RC]\d+/).each_with_object([[],[]]) {|n,(r,c)|
(n[0]=='R' ? r : c) << n[1..-1].to_i}
Here's what's happening. Suppose:
s = "R1C4R2C5R32R4C7R18C6C12"
Then
a = s.scan(/[RC]\d+/)
#=> ["R1", "C4", "R2", "C5", "R32", "R4", "C7", "R18", "C6", "C12"]
scan uses the regex /([RC]\d+)/ to extract substrings that begin with 'R' or 'C' followed by one or more digits up to the next letter or end of the string.
b = a.each_with_object([[],[]]) {|n,(r,c)|(n[0]=='R' ? r : c) << n[1..-1].to_i}
#=> [[1, 2, 32, 4, 18], [4, 5, 7, 6, 12]]
The row values are given by [1, 2, 32, 4, 18]; the column values by [4, 5, 7, 6, 12].
Enumerable#each_with_object (v1.9+) creates an array comprised of two empty arrays, [[],[]]. The first subarray will contain the row values, the second, the column values. These two subarrays are represented by the block variables r and c, respectively.
The first element of a is "R1". This is represented in the block by the variable n. Since
"R1"[0] #=> "R"
"R1"[1..-1] #=> "1"
we execute
r << "1".to_i #=> [1]
so now
[r,c] #=> [[1],[]]
The next element of a is "C4", so we will execute:
c << "4".to_i #=> [4]
so now
[r,c] #=> [[1],[4]]
and so on.
rows, cols = "R1C4R2C5".scan(/R(\d+)C(\d+)/).flatten.partition.with_index {|_, index| index.even? }
> rows
=> ["1", "2"]
> cols
=> ["4", "5"]
Or
rows = "R1C4R2C5".scan(/R(\d+)/).flatten
=> ["1", "2"]
cols = "R1C4R2C5".scan(/C(\d+)/).flatten
=> ["4", "5"]
And to fix your code use:
word2.each {|a| col.push(a.delete('C')) if a.include? 'C' }
word2.each {|a| row.push(a.delete('R')) if a.include? 'R' }
I'm attempting to create a word generator based on a 4x4 grid of letters (below).
Here are the rules:
Letters cannot be repeated
Words must be formed by adjacent letters
Words can be formed horizontally, vertically or diagonally to the left, right or up-and-down
Currently, I take a 16-character input and loop through every word in the dictionary, determining whether that word can be spelled with the letters on the grid.
#!/usr/bin/ruby
require './scores' # alphabet and associated Scrabble scoring value (ie the wordValue() method)
require './words.rb' # dictionary of English words (ie the WORDS array)
# grab users letters
puts "Provide us the 16 letters of your grid (no spaces please)"
word = gets.chomp.downcase
arr = word.split('')
# store words that can be spelled with user's letters
success = []
# iterate through dictionary of words
WORDS.each do |w|
# create temp arrays
dict_arr = w.split('')
user_arr = arr.dup
test = true
# test whether users letters spell current word in dict
while test
dict_arr.each do |letter|
if (user_arr.include?(letter))
i = user_arr.index(letter)
user_arr.delete_at(i)
else
test = false
break
end
end
# store word in array
if test
success << w
test = false
end
end
end
# create hash for successful words and their corresponding values
SUCCESS = {}
success.each do |w|
score = wordValue(w)
SUCCESS[w] = score
end
# sort hash from lowest to smallest value
SUCCESS = SUCCESS.sort_by {|word, value| value}
# print results to screen
SUCCESS.each {|k,v| puts "#{k}: #{v}"}
However, this approach doesn't take into account the positions of the tiles on the board. How would you suggest I go about finding words that can be created based on their location in the 4x4 grid?
For the board game in the image above, it takes about 1.21 seconds for my VM running Ubuntu to compute the 1185 possible words. I'm using the dictionary of words provided with Ubunut in /usr/share/dict/words
Instead of iterating over words and searching for their presence, walk through each tile on the grid and find all words stemming from that tile.
First, compile your dictionary into a trie. Tries are efficient at performing prefix-matching string comparisons, which will be of use to us shortly.
To find the words within the board, perform the following steps for each of the 16 tiles, starting with an empty string for prefix.
Add the current tile's value to prefix.
Check if our trie contains any words starting with prefix.
If it does, branch the search: for each legal (unvisited) tile that is adjacent to this tile, go back to step 1 (recurse).
If it doesn't match, stop this branch of the search since there are no matching words.
I would create a simple graph representing the whole board. Letters would be vertices. If two letters are near one another on the board I would create an edge between their vertices. It would be very easy to find out whether the input is valid. You simply would have to check whether there is a matching path in the graph.
My original answer was not what you wanted. I was creating a list of all of the
"words" in the grid, instead of searching for the words you had already identified
from the dictionary. Now I have written a function which searches the grid for a
particular word. It works recursively.
So, now the algorithm is:
1) Get the 16 letters from the user
2) Search dictionary for all words with those letters
3) Call is_word_on_board with each of those words to see if you have a match
#!/usr/bin/ruby
# This script searches a board for a word
#
# A board is represented by a string of letters, for instance, the string
# "abcdefghijklmnop" represents the board:
#
# a b c d
# e f g h
# i j k l
# m n o p
#
# The array ADJACENT lists the cell numbers that are adjacent to another
# cell. For instance ADJACENT[3] is [2, 6, 7]. If the cells are numbered
#
# 0 1 2 3
# 4 5 6 7
# 8 9 10 11
# 12 13 14 15
ADJACENT = [
[1, 4, 5],
[0, 2, 4, 5, 6],
[1, 3, 5, 6, 7],
[2, 6, 7],
[0, 1, 5, 8, 9],
[0, 1, 2, 4, 6, 8, 9, 10],
[1, 2, 3, 5, 7, 9, 10, 11],
[2, 3, 6, 10, 11],
[4, 5, 9, 12, 13],
[4, 5, 6, 8, 10, 12, 13, 14],
[5, 6, 7, 9, 11, 13, 14, 15],
[6, 7, 10, 14, 15],
[8, 9, 13],
[8, 9, 10, 12, 14],
[9, 10, 11, 13, 15],
[10, 11, 14]
]
# function: is_word_on_board
#
# parameters:
# word - word you're searching for
# board - string of letters representing the board, left to right, top to bottom
# prefix - partial word found so far
# cell - position of last letter chosen on the board
#
# returns true if word was found, false otherwise
#
# Note: You only need to provide the word and the board. The other two parameters
# have default values, and are used by the recursive calls.
# set this to true to log the recursive calls
DEBUG = false
def is_word_on_board(word, board, prefix = "", cell = -1)
if DEBUG
puts "word = #{word}"
puts "board = #{board}"
puts "prefix = #{prefix}"
puts "cell = #{cell}"
puts
end
# If we're just beginning, start word at any cell containing
# the starting letter of the word
if prefix.length == 0
0.upto(15) do |i|
if word[0] == board[i]
board_copy = board.dup
newprefix = board[i,1]
# put "*" in place of letter so we don't reuse it
board_copy[i] = ?*
# recurse, and return true if the word is found
if is_word_on_board(word, board_copy, newprefix, i)
return true
end
end
end
# we got here without finding a match, so return false
return false
elsif prefix.length == word.length
# we have the whole word!
return true
else
# search adjacent cells for the next letter in the word
ADJACENT[cell].each do |c|
# if the letter in this adjacent cell matches the next
# letter of the word, add it to the prefix and recurse
if board[c] == word[prefix.length]
newprefix = prefix + board[c, 1]
board_copy = board.dup
# put "*" in place of letter so we don't reuse it
board_copy[c] = ?*
# recurse, and return true if the word is found
if is_word_on_board(word, board_copy, newprefix, c)
return true
end
end
end
# bummer, no word found, so return false
return false
end
end
puts "Test board:"
puts
puts " r u t t"
puts " y b s i"
puts " e a r o"
puts " g h o l"
puts
board = "ruttybsiearoghol"
for word in ["ruby", "bears", "honey", "beast", "rusty", "burb", "bras", "ruttisbyearolohg", "i"]
if is_word_on_board(word, board)
puts word + " is on the board"
else
puts word + " is NOT on the board"
end
end
Running this script give the following results:
Test board:
r u t t
y b s i
e a r o
g h o l
ruby is on the board
bears is on the board
honey is NOT on the board
beast is on the board
rusty is NOT on the board
burb is NOT on the board
bras is on the board
ruttisbyearolohg is on the board
i is on the board
I happen to have a Boggle solver I wrote a while ago. It follows Cheeken's outline. It's invoked a bit differently (you supply the word list file and a text file with a 4x4 grid as arguments), but I figured it was worth sharing. Also note that it treats "Q" as "QU", so there's some extra logic in there for that.
require 'set'
def build_dict(dict, key, value)
if key.length == 0
dict[:a] = value
else
if key[0] == "q"
first = key[0..1]
rest = key[2, key.length - 1]
else
first = key[0]
rest = key[1, key.length - 1]
end
dict[first] = {} unless dict.has_key? first
build_dict(dict[first], rest, value)
end
end
dict = {}
#parse the file into a dictionary
File.open(ARGV[0]).each_line do |line|
real_line = line.strip
build_dict(dict, real_line, real_line)
end
#parse the board
board = {}
j = 0
File.open(ARGV[1]).each_line do |line|
line.chars.each_with_index do |l, i|
board[[j, i]] = l
end
j += 1
end
#(0..3).each{|r| puts (0..3).map{|c| board[[r, c]]}.join}
#how can i get from one place to another?
def get_neighbors(slot, sofar)
r, c = slot
directions =
[
[r+1, c],
[r+1, c+1],
[r+1, c-1],
[r, c+1],
[r, c-1],
[r-1, c],
[r-1, c+1],
[r-1, c-1]
]
directions.select{|a| a.all?{|d| d >= 0 && d <= 3} && !sofar.include?(a)}
end
#actual work
def solve(board, slot, word_dict, sofar)
results = Set.new
letter = board[slot]
letter = "qu" if letter == "q"
stuff = word_dict[letter]
return results if stuff.nil?
if stuff.has_key? :a
results << stuff[:a] if stuff[:a].length > 2
end
unless stuff.keys.select{|key| key != :a}.empty?
get_neighbors(slot, sofar).each do |dir|
results += solve(board, dir, stuff, sofar.clone << slot)
end
end
results
end
#do it!
results = Set.new
all_slots = (0..3).to_a.product((0..3).to_a)
all_slots.each do |slot|
results += solve(board, slot, dict, slot)
end
puts results.sort