Infinite loop nested within a block in Ruby - ruby

Here's what I'm trying to do. I'm iterating through an array of strings, each of which may contain [] multiple times. I want to use String#match as many times as necessary to process every occurrence. So I have an Array#each block, and nested within that is an infinite loop that should break only when I run out of matches for a given string.
def follow(c, dir)
case c
when "\\"
dir.rotate!
when "/"
dir.rotate!.map! {|x| -x}
end
return dir
end
def parse(ar)
ar.each_with_index { |s, i|
idx = 0
while true do
t = s.match(/\[[ ]*([a-zA-Z]+)[ ]*\]/) { |m|
idx = m.end 0
r = m[1]
s[m.begin(0)...idx] = " "*m[0].size
a = [i, idx]
dir = [0, 1]
c = ar[a[0]][a[1]]
while !c.nil? do
dir = follow c, dir
ar[a[0]][a[1]] = " "
a[0] += dir[0]; a[1] += dir[1]
c = ar[a[0]][a[1]]
if c == ">" then
ar[a[0]][a[1]+1] = r; c=nil
elsif c == "<" then
ar[a[0]][a[1]-r.size] = r; c=nil
end
end
ar[a[0]][a[1]] = " "
puts ar
}
if t == nil then break; end
end
}
parse File.new("test", "r").to_a
Contents of test:
+--------+----------+-------------+
| Colors | Foods | Countries |
+--------+----------+-------------+
| red | pizza | Switzerland |
/--> /----> | |
| |[kale]/ | hot dogs | Brazil |
| | <----------------------\ |
| | orange |[yellow]\ | [green]/ |
| +--------+--------|-+-------------+
\-------------------/
Goal:
+--------+----------+-------------+
| Colors | Foods | Countries |
+--------+----------+-------------+
| red | pizza | Switzerland |
yellow kale | |
| | hot dogs | Brazil |
| green |
| orange | | |
+--------+-------- -+-------------+
Actual output of program:
+--------+----------+-------------+
| Colors | Foods | Countries |
+--------+----------+-------------+
| red | pizza | Switzerland |
yellow kale | |
| | hot dogs | Brazil |
| <----------------------\ |
| orange | | [green]/ |
+--------+-------- -+-------------+
(Since the array is modified in place, I figure there is no need to update the match index.) The inner loop should run again for each pair of [] I find, but instead I only get one turn per array entry. (It seems that the t=match... bit isn't the problem, but I could be wrong.) How can I fix this?

Related

Optimal algorithm to determine relationship tree

Suppose that you have a list of formulas like the following one:
KPI1 = somevalue1 + somevalue2
KPI2 = somevalue1 + somevalue3
KPI3 = KPI1 + somevalue4
KPI4 = KPI2 + KPI3
etc.
Which is the optimal algorithm that can be used to obtain a relationship tree for each of the elements referenced in the formulas?
i.e., using the example above:
+------------------------------------------------------------+
| somevalue3 somevalue1 somevalue2 somevalue4 |
| | | | | | |
| --------------- -------------- | |
| | | | |
| KPI2 KPI1 | |
| | | | |
| | --------------------- |
| | | |
| | KPI3 |
| | | |
| --------------------------- |
| | |
| KPI4 |
+------------------------------------------------------------+
You can use a hashmap where a key corresponds to a defined name (e.g. "KPI3"), and the corresponding value is a list of names/values on which that name depends (e.g. ["KPI1", somevalue4]).
Here is an implementation in JavaScript, where we can use the native Map constructor. Once the map is populated with all the dependencies, a recursive function can for example print the tree:
function printTree(map, name, indent="") {
console.log(indent + name);
let children = map.get(name);
if (children !== undefined) {
for (let childName of children) {
printTree(map, childName, indent+" ");
}
}
}
let map = new Map();
map.set("KPI1", ["value1", "value2"]);
map.set("KPI2", ["value1", "value3"]);
map.set("KPI3", ["KPI1", "value4"]);
map.set("KPI4", ["KPI2", "KPI3"]);
printTree(map, "KPI4");
In Python you would use a dictionary:
def print_tree(d, name, indent=""):
print(indent + name)
children = d.get(name, None)
if children is not None:
for childName in children:
print_tree(d, childName, indent+" ")
d = dict()
d["KPI1"] = ["value1", "value2"]
d["KPI2"] = ["value1", "value3"]
d["KPI3"] = ["KPI1", "value4"]
d["KPI4"] = ["KPI2", "KPI3"]
print_tree(d, "KPI4")

How to draw a square grid of size n to console?

I need to draw a square grid of size n to the console. The grid uses - for horizontal cell boundaries, | for vertical cell boundaries, and + for corners of each cell.
As an example, a size 3 grid should look like the following:
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
I was thinking of using a double for loop with outer loop iterating through rows and inner loop iterating through cols. each iteration of inner loop would be processing an individual cell. drawing | characters doesn't seem to hard but I'm not sure how I'd go about printing the - chars above and below a cell.
You could use Integer#times and String#*:
def print_grid(n)
n.times { print "+-"*n, "+\n", "| "*n, "|\n" }
print "+-"*n, "+\n"
end
print_grid(3)
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
=> nil
Alternatively:
def print_grid(n)
puts n.times.map{ "+-"*n + "+\n" + "| "*n + "|\n" }.join + "+-"*n + "+\n"
end
For a width of 3, you have a separator-row like this:
+-+-+-+
This can be sees as either:
3 - connected by and surrounded by +
4 + connected by -
The latter is a bit easier to express in Ruby:
width = 3
Array.new(width + 1, '+').join('-')
#=> "+-+-+-+"
The same works for the cell-row:
Array.new(width + 1, '|').join(' ')
#=> "| | | |"
Vertically, you have 3 cell-rows connected by and surrounded by separator-rows. (that should ring a bell) Just like before, this can also be expressed as 4 separator-rows connected by cell-rows.
Let's store our separator-row and cell-row in variables: (we also have to append newlines)
width = 3
separator_row = Array.new(width + 1, '+').join('-') << "\n"
cell_row = Array.new(width + 1, '|').join(' ') << "\n"
And define the grid:
height = 3
grid = Array.new(height + 1, separator_row).join(cell_row)
#=> "+-+-+-+\n| | | |\n+-+-+-+\n| | | |\n+-+-+-+\n| | | |\n+-+-+-+\n"
put grid
Output:
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
Here are two ways to do that.
Determine each character depending on whether the row and column indices are even or odd
def grid(n)
sz = 2*n+1
Array.new(sz) do |i|
Array.new(sz) do |j|
if i.even?
j.even? ? '+' : '-'
else # i is odd
j.even? ? '|' : ' '
end
end.join
end
end
puts grid(3)
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
puts grid(4)
+-+-+-+-+
| | | | |
+-+-+-+-+
| | | | |
+-+-+-+-+
| | | | |
+-+-+-+-+
| | | | |
+-+-+-+-+
Use an enumerator
def pr_grid(n)
enum = [*['+','-']*n, "+", "\n", *['|',' ']*n, "|", "\n"].cycle
((2+2*n)*(1+2*n)).times { print enum.next }
end
pr_grid(3)
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
| | | |
+-+-+-+
pr_grid(4)
+-+-+-+-+
| | | | |
+-+-+-+-+
| | | | |
+-+-+-+-+
| | | | |
+-+-+-+-+
| | | | |
+-+-+-+-+
For n = 3 the steps are as follows.
a = [*['+','-']*n, "+", "\n", *['|',' ']*n, "|", "\n"]
#=> ["+", "-", "+", "-", "+", "-", "+", "\n",
# "|", " ", "|", " ", "|", " ", "|", "\n"]
enum = a.cycle
#=> #<Enumerator: ["+", "-", "+", "-", "+", "-", "+", "\n",
# "|", " ", "|", " ", "|", " ", "|", "\n"]:cycle
enum.next #=> "+"
enum.next #=> "-"
enum.next #=> "+"
enum.next #=> "-"
enum.next #=> "+"
enum.next #=> "-"
enum.next #=> "+"
enum.next #=> "\n"
enum.next #=> "|"
enum.next #=> " "
and so on.
Just for fun and just as an example, here is a list of methods (to be optimized and debugged and maybe used within a class) that you can use also for filling a table.
#cell = '|'
#line = '-'
#cross = '+'
def build_row(content)
(content.zip [#cell]* content.size).flatten.prepend(#cell).join
end
def separator_from_content(content)
content.map { |e| #line * e.size + #cross }.prepend(#cross).join
end
def adjust_content_in(lines)
width = lines.flatten.max_by(&:size).size
lines.map { |line| line.map { |e| e << ' ' * (width - e.size) } }
end
def build_table(lines)
lines = adjust_content_in(lines)
separator = separator_from_content(lines.first)
mapped = lines.map { |line| build_row(line) }.zip([separator] * (lines.size) )
return mapped.flatten.prepend(separator).join("\n")
end
def empty_squared_table(n)
lines = n.times.map { n.times.map { ' ' } }
build_table(lines)
end
So, if you want to draw an empty table, can call:
n = 3
puts empty_squared_table(n)
# +-+-+-+
# | | | |
# +-+-+-+
# | | | |
# +-+-+-+
# | | | |
# +-+-+-+
Or you can fill in some text (more lines inside a cell not supported):
lines = [['so', 'you can', 'fill'], ['a table', 'given the content', '']]
puts build_table(lines)
# +-----------------+-----------------+-----------------+
# |so |you can |fill |
# +-----------------+-----------------+-----------------+
# |a table |given the content| |
# +-----------------+-----------------+-----------------+

Ruby adding empty strings to hash for CSV spacing

I have:
hash = {"1"=>["A", "B", "C", ... "Z"], "2"=>["B", "C"], "3"=>["A", "C"]
My goal is to use hash as a source for creating a CSV with columns whose names are a letter of the alphabet and with rows hash(key) = 1,2,3 etc.
I created an array of all hash.values.unshift("")values that serve as row 1 (columns labels).
desired output:
| A | B | C | ... | Z |
1| A | B | C | ... | Z |
2| | B | C | ....... |
3| A | | C | ....... |
Creating CSV:
CSV.open("groups.csv", 'w') do |csv|
csv << row1
hash.each do |v|
csv << v.flatten
end
end
This makes the CSV look almost what I want but There is no spacing to get columns to align.
Any advice on how to make a method for modifying my hash that compares my all [A-Z] against each subsequent hash key (rows) to insert empty strings to provide spacing?
Can Class CSV do it better?
Something like this?
require 'csv'
ALPHA = ('A'..'Z').to_a.freeze
hash={"1"=>ALPHA, "2"=>["B", "C"], "3"=>["A", "C"]}
csv = CSV.generate("", col_sep: "|") do |csv|
csv << [" "] + ALPHA # header
hash.each do |k, v|
alphabet = ALPHA.map { |el| [el, 0] }.to_h
v.each { |el| alphabet[el] += 1 }
csv << [k, *alphabet.map { |k, val| val == 1 ? k : " " }]
end
end
csv.split("\n").each { |row| puts row }
output:
|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z
1|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z
2| |B|C| | | | | | | | | | | | | | | | | | | | | | |
3|A| |C| | | | | | | | | | | | | | | | | | | | | | |
If your values are truly single characters and don't need the CSV escaping, then I recommend bypassing CSV altogether and building the string in plain Ruby.
Assuming you want to align your lines correctly regardless of the number of digits in the row number (e.g. 1, 10, and 100), you can use printf style formatting to guarantee horizontal aligment (assuming your row number width never exceeds the value of ROWNUM_WIDTH).
By the way, I changed the hash's keys to integers, hope that's ok.
#!/usr/bin/env ruby
FIELDS = ('A'..'Z').to_a
DATA = { 1 => FIELDS, 2 => %w(B C), 3 => %w(A C) }
ROWNUM_WIDTH = 3
output = ' ' * ROWNUM_WIDTH + " | #{FIELDS.join(' | ')} |\n"
DATA.each do |rownum, values|
line = "%*d | " % [ROWNUM_WIDTH, rownum]
FIELDS.each do |field|
char = values.include?(field) ? field : ' '
line << "#{char} | "
end
output << line << "\n"
end
puts output
=begin
Outputs:
| A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
1 | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
2 | | B | C | | | | | | | | | | | | | | | | | | | | | | | |
3 | A | | C | | | | | | | | | | | | | | | | | | | | | | | |
=end
all = [*?A..?Z]
hash = {"1"=>[*?A..?Z], "2"=>["B", "C"], "3"=>["A", "C"]}
hash.map do |k, v|
[k, *all.map { |k| v.include?(k) ? k : ' ' }]
end.unshift([' ', *all]).
map { |row| row.join('|') }
#⇒ [
# [0] " |A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z",
# [1] "1|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z",
# [2] "2| |B|C| | | | | | | | | | | | | | | | | | | | | | | ",
# [3] "3|A| |C| | | | | | | | | | | | | | | | | | | | | | | "
# ]

Ruby: trouble with batch variable assignment

Yesterday, I started making a chess program and, trying to save 60-odd lines, decided to try my hand at batch variable assignment. That is, assigning variables through loops. I can't seem to join spot_def_letters[i] with o_s in the correct fashion, though. ($board is scoped for later use)
My code:
spot_def_letters = ["a", "b", "c", "d", "e", "f", "g", "h"]
8.times do
i = 0
o = 1
8.times do
o_s = o.to_s
spot_def_letters[i] + o_s = " "
o += 1
end
end
$board = """
| | | | | | |
#{a8}|#{b8}|#{c8}|#{d8}|#{e8}|#{f8}|#{g8}|#{h8}
| | | | | | |
#{a7}|#{b7}|#{c7}|#{d7}|#{e7}|#{f7}|#{g7}|#{h7}
| | | | | | |
#{a6}|#{b6}|#{c6}|#{d6}|#{e6}|#{f6}|#{g6}|#{h6}
| | | | | | |
#{a5}|#{b5}|#{c5}|#{d5}|#{e5}|#{f5}|#{g5}|#{h5}
| | | | | | |
#{a4}|#{b4}|#{c4}|#{d4}|#{e4}|#{f4}|#{g4}|#{h4}
| | | | | | |
#{a3}|#{b3}|#{c3}|#{d3}|#{e3}|#{f3}|#{g3}|#{h3}
| | | | | | |
#{a2}|#{b2}|#{c2}|#{d2}|#{e2}|#{f2}|#{g2}|#{h2}
| | | | | | |
#{a1}|#{b1}|#{c1}|#{d1}|#{e1}|#{f1}|#{g1}|#{h1}
"""
instead of indexing into string arrays and using a bunch of sneaky, bad-looking code, I decided to use a 64-value array (spots = Array.new(64, " ")) and puts those values.

ruby - can't concatenate array elements into a string?

For personal homework I am trying out a digital clock program to display 'big' numbers.
A ruby program that has parts of the clock that come from strings stored in an array and then a routine (not yet fully written) that can display any numbers as 'big numbers'.
This is to help me learn more about manipulating hashes and iterating through arrays, concatenating string, etc.
I am stuck right now with this:
all_nums=''
(0..3).each do |num|
all_nums+= (line_parts[num][0].values).to_s
#puts line_parts[num][0].values
end
puts all_nums
because I am getting back array elements that I can't convert to strings, i.e.
ruby digital_clock,rb
[" == "]["| | "]["| | "][" == "]
but when I just puts them (commented out 3rd line from the bottom) it is OK but, I get strings, i.e.
ruby digital_clock.rb
==
| |
| |
==
I want to be able to build up the string (big 0 in this case) but I can only puts out the pieces and I've been unable to assign them to a variable and print it. How could I do that?
The full program is:
top= ' == | == == | | == | == == == '
top_middle= '| | | __| __| |__| |__ |__ | |__| |__|'
bottom_middle='| | | | | | | | | | | | |'
bottom= ' == | == == | == == | == |'
number_parts=[{},{},{},{},{},{},{},{},{},{}]
line_parts=[[],[],[],[]]
['top','top_middle','bottom_middle','bottom'].each_with_index do |one_line, i|
number_parts=[{},{},{},{},{},{},{},{},{},{}]
(0..9).each do |the_num|
number_parts[the_num] = {one_line.to_sym => eval(one_line)[the_num*5,5].to_s}
end
line_parts[i]= number_parts
end
all_nums=''
(0..3).each do |num|
all_nums+= (line_parts[num][0].values).to_s
#puts line_parts[num][0].values
end
puts all_nums
I think this will fix your immediate problem -- namely, printing a big "0".
all_nums = []
(0..3).each do |num|
all_nums.push(line_parts[num][0].values)
end
puts all_nums.join("\n")
A better way to do that would be with map:
all_nums = (0..3).map { |num| line_parts[num][0].values }
puts all_nums.join("\n")
Another issue: you don't need to use eval. In fact, one almost never needs to use eval.
# Use variables rather than the strings 'top', 'top_middle', etc.
[top,top_middle,bottom_middle,bottom].each_with_index do |one_line, i|
...
# Then the eval() is unnecessary.
number_parts[the_num] = {one_line.to_sym => one_line[the_num*5,5]}
....
But you can really simplify the code along the following lines:
# Use an array, not a set of specifically named variables.
# This makes it a lot easier to iterate over the data.
rows = [
' -- | -- -- | | -- | -- -- -- ',
' | | | __| __| |__| |__ |__ | |__| |__|',
' | | | | | | | | | | | | |',
' -- | -- -- | -- -- | -- |',
]
# Create an array-of-arrays.
# num_parts[NUMBER] = [an array of four strings]
width = 5
num_parts = (0..9).map { |n| rows.map { |r| r[n * width, width] } }
# Inspect.
num_parts.each do |np|
puts
puts np.join("\n")
end
You might also take a look at the Unix command called banner, which will inspire you to make the numbers EVEN BIGGER. That would allow you to start adding curves and other design touches.

Resources