Решение на Осма задача от Георги Стефанов

Обратно към всички решения

Към профила на Георги Стефанов

Резултати

  • 5 точки от тестове
  • 0 бонус точки
  • 5 точки общо
  • 35 успешни тест(а)
  • 5 неуспешни тест(а)

Код

class Spreadsheet
attr_accessor :grid, :cells
def initialize(grid = nil)
@grid = ""
@grid = Spreadsheet.cells(grid) if grid
@cells = @grid.flatten.each_with_index.
map { |cell, index| Cell.new(cell, index, self) } if grid
@cells.each {|cell| cell.sheet = self} if grid
end
def empty?
@grid.empty?
end
def cell_at(cell_index)
raise Error, "Invalid cell index '#{ cell_index }'" \
if not Index.valid_index?(cell_index)
cell = @cells.find { |target| target.index_string == cell_index }
raise Error, "Cell '#{ cell_index }' does not exist" if cell == nil
cell.content if cell
end
def [](cell_index)
raise Error, "Invalid cell index '#{ cell_index }'" \
if not Index.valid_index?(cell_index)
cell = @cells.find { |target| target.index_string == cell_index }
raise Error, "Cell '#{ cell_index }' does not exist" if cell == nil
Formula.beautify_s(cell.evaluate) if cell
end
def self.cells(grid)
rows = grid.strip.split(/[\n]/)
rows.map! { |row| row.split(/\s{2,}|\t/) }
rows.map{ |row| row.delete_if { |cell| cell =~ /^\s*$/ } }
rows
end
def to_s
sheet = @cells.map { |cell| cell.evaluate }
sheet = sheet.each_slice(@grid[0].size).to_a
sheet.map! { |row| row.join("\t") }
sheet.join("\n")
end
class Error < Exception
end
class Index
attr_accessor :column, :row
def self.column(number)
return "" if number == 0
number % 26 != 0 ? remainder = number % 26 : remainder = 26
number -= 26 if remainder == 26
number /= 26
return "#{ self.column(number) }" + "#{ self.to_letter(remainder) }"
end
def self.to_letter(number)
letters = [*"A".."Z"]
letter = letters.find { |letter| letter.ord - 64 == number }
return letter
end
def self.is_number?(number_string)
not number_string.match(/\A[-]?\d+(\.\d+)?\Z/) == nil
end
def self.row(number)
number.to_s
end
def self.valid_index?(string)
string.match(/\A[A-Z]+[1-9]+\z/) != nil
end
def initialize(number, rows, columns)
@column = Index.column(number % columns + 1)
@row = Index.row(number / columns + 1)
end
end
class Expressions
attr_accessor :cell, :sheet, :expression
def initialize(expression, cell)
@cell = cell
@sheet = @cell.sheet
@expression = expression[1...expression.size].strip
end
def evaluate
return Formula.beautify_s(cell.sheet[find_key]) if is_cell?
return Formula.beautify(find_key.to_f) if is_number?
raise Error, "Invalid expression '#{ expression }'" if not is_valid?
raise Error, "Unknown function '#{ find_key }'" if formula? == nil
Formula.new(sheet, arguments).method(formula?.downcase).call
end
def is_cell?
expression =~ (/\A *[A-Z]+[0-9]+ *\z/)
end
def is_number?
expression =~ (/\A *[-]?\d+(\.\d+)?\Z/)
end
def is_valid?
(expression.match(%r{\A\ *[A-Z]+\ *\(\ *(([-]?\d+(\.\d+)?)|
([A-Z]+[0-9]+))?\ *\)\z}x) != nil) || \
(expression.match(%r{\A\ *[A-Z]+\ *\(\ *((([-]?\d+(\.\d+)?)|
([A-Z]+[0-9]+))\ *,\ *)+\ *(([-]?\d+(\.\d+)?)
|([A-Z]+[0-9]+))\ *\)\ *\z}x) != nil)
end
def formula?
name = Formula.class_eval("@@names").
find { |formula| formula == find_key }
end
def arguments
arguments = expression.split(/[()]/)[1].strip
arguments
end
def find_key
key = expression.match(/[A-Z]+[0-9]+/).to_s if is_cell?
key = expression.match(/[-]?\d+(\.\d+)?/).to_s if is_number?
key = expression.match(/[A-Z]+/).to_s if not (is_cell? or is_number?) \
and is_valid?
key
end
end
class Cell
attr_accessor :content, :index, :sheet, :expression
def initialize(content, index, sheet)
@sheet = sheet
@content = content
@index = Index.new(index, sheet.grid.size, sheet.grid[0].size)
@expression = Expressions.new(content, self) if is_expression?
end
def is_expression?
content[0] == "="
end
def evaluate
return content if not is_expression?
@expression.evaluate
end
def index_string
@index.column + @index.row
end
end
class Formula
@@names = ["ADD", "MULTIPLY", "SUBTRACT", "DIVIDE", "MOD"]
attr_accessor :sheet, :arguments
def add
raise Error, "Wrong number of arguments for 'ADD': " \
"expected at least 2, got #{ arguments.size }" \
if arguments.size < 2
sum = arguments.map { |argument| argument.to_f }.reduce(:+)
Formula.beautify(sum)
end
def multiply
raise Error, "Wrong number of arguments for 'MULTIPLY': " \
"expected at least 2, got #{ arguments.size }" \
if arguments.size < 2
product = arguments.map { |argument| argument.to_f }.reduce(:*)
Formula.beautify(product)
end
def subtract
raise Error, "Wrong number of arguments for 'SUBTRACT': " \
"expected 2, got #{ arguments.size }" \
if arguments.size != 2
difference = arguments[0].to_f - arguments[1].to_f
Formula.beautify(difference)
end
def divide
raise Error, "Wrong number of arguments for 'DIVIDE': " \
"expected 2, got #{ arguments.size }" \
if arguments.size != 2
ratio = arguments[0].to_f / arguments[1].to_f
Formula.beautify(ratio)
end
def mod
raise Error, "Wrong number of arguments for 'MOD'" \
": expected 2, got #{arguments.size}" \
if arguments.size != 2
remainder = arguments[0].to_f % arguments[1].to_f
Formula.beautify(remainder)
end
def initialize(sheet, arguments)
@sheet = sheet
@arguments = arguments.split(",").each { |argument| argument.strip! }
@arguments.map! do |argument|
Index.is_number?(argument) ? argument : sheet[argument]
end
end
def self.beautify(number)
number_s = number.round(2)
return "#{ number_s.to_i }" if number_s == number_s.to_i
number_s = number_s.to_s.split(/[.]/)
number_s[1] = number_s[1] + "0" * (2 - number_s[1].size)
number_s.join(".")
end
def self.beautify_s(string)
output = string.strip
is_number = Index.is_number?(output)
is_number ? Formula.beautify(output.to_f) : output
end
end
end

Лог от изпълнението

....F.....F.F.....F...F.................

Failures:

  1) Spreadsheet#to_s returns blank tables as blank strings
     Failure/Error: expect(Spreadsheet.new.to_s).to eq ''
     NoMethodError:
       undefined method `map' for nil:NilClass
     # /tmp/d20160121-5693-ouzkky/solution.rb:39:in `to_s'
     # /tmp/d20160121-5693-ouzkky/spec.rb:24:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  2) Spreadsheet#cell_at raises and exception for non-existant cells
     Failure/Error: expect { Spreadsheet.new('foo')['B10'] }.to raise_error(Spreadsheet::Error, /Cell 'B10' does not exist/)
       expected Spreadsheet::Error with message matching /Cell 'B10' does not exist/, got #<Spreadsheet::Error: Invalid cell index 'B10'> with backtrace:
         # /tmp/d20160121-5693-ouzkky/solution.rb:24:in `[]'
         # /tmp/d20160121-5693-ouzkky/spec.rb:59:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-ouzkky/spec.rb:59:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-ouzkky/spec.rb:59:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  3) Spreadsheet#[] raises an exception for non-existant cells
     Failure/Error: expect { Spreadsheet.new()['A1'] }.to raise_error(Spreadsheet::Error, /Cell 'A1' does not exist/)
       expected Spreadsheet::Error with message matching /Cell 'A1' does not exist/, got #<NoMethodError: undefined method `find' for nil:NilClass> with backtrace:
         # /tmp/d20160121-5693-ouzkky/solution.rb:26:in `[]'
         # /tmp/d20160121-5693-ouzkky/spec.rb:75:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-ouzkky/spec.rb:75:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-ouzkky/spec.rb:75:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  4) Spreadsheet#[] raises an exception for less than two arguments passed to ADD
     Failure/Error: expect { Spreadsheet.new('=ADD()')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'ADD': expected at least 2, got 0/, got #<NoMethodError: undefined method `strip' for nil:NilClass> with backtrace:
         # /tmp/d20160121-5693-ouzkky/solution.rb:122:in `arguments'
         # /tmp/d20160121-5693-ouzkky/solution.rb:97:in `evaluate'
         # /tmp/d20160121-5693-ouzkky/solution.rb:150:in `evaluate'
         # /tmp/d20160121-5693-ouzkky/solution.rb:28:in `[]'
         # /tmp/d20160121-5693-ouzkky/spec.rb:123:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-ouzkky/spec.rb:123:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-ouzkky/spec.rb:123:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  5) Spreadsheet#[] raises an exception for less than two arguments to MULTIPLY
     Failure/Error: expect { Spreadsheet.new('=MULTIPLY()')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'MULTIPLY': expected at least 2, got 0/, got #<NoMethodError: undefined method `strip' for nil:NilClass> with backtrace:
         # /tmp/d20160121-5693-ouzkky/solution.rb:122:in `arguments'
         # /tmp/d20160121-5693-ouzkky/solution.rb:97:in `evaluate'
         # /tmp/d20160121-5693-ouzkky/solution.rb:150:in `evaluate'
         # /tmp/d20160121-5693-ouzkky/solution.rb:28:in `[]'
         # /tmp/d20160121-5693-ouzkky/spec.rb:153:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-ouzkky/spec.rb:153:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'
     # /tmp/d20160121-5693-ouzkky/spec.rb:153:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.06274 seconds
40 examples, 5 failures

Failed examples:

rspec /tmp/d20160121-5693-ouzkky/spec.rb:23 # Spreadsheet#to_s returns blank tables as blank strings
rspec /tmp/d20160121-5693-ouzkky/spec.rb:58 # Spreadsheet#cell_at raises and exception for non-existant cells
rspec /tmp/d20160121-5693-ouzkky/spec.rb:74 # Spreadsheet#[] raises an exception for non-existant cells
rspec /tmp/d20160121-5693-ouzkky/spec.rb:118 # Spreadsheet#[] raises an exception for less than two arguments passed to ADD
rspec /tmp/d20160121-5693-ouzkky/spec.rb:148 # Spreadsheet#[] raises an exception for less than two arguments to MULTIPLY

История (6 версии и 0 коментара)

Георги обнови решението на 10.01.2016 20:44 (преди над 8 години)

+class Spreadsheet
+ attr_accessor :grid, :cells
+ def initialize(grid = nil)
+ @grid = ""
+ @grid = Spreadsheet.cells(grid) if grid
+ @cells = @grid.flatten.each_with_index.
+ map {|cell, index| Cell.new(cell, index, self)} if grid
+ @cells.each {|cell| cell.sheet = self} if grid
+ end
+
+ def empty?
+ @grid.empty?
+ end
+
+ def cell_at(cell_index)
+ raise Error, "Invalid cell index '#{cell_index}'" \
+ if not Index.valid_index?(cell_index)
+ cell = @cells.find { |target| target.index_string == cell_index }
+ raise Error, "Cell '#{cell_index}' does not exist" if cell == nil
+ cell.content if cell
+ end
+
+ def [](cell_index)
+ raise Error, "Invalid cell index '#{cell_index}'" \
+ if not Index.valid_index?(cell_index)
+ cell = @cells.find { |target| target.index_string == cell_index }
+ raise Error, "Cell '#{cell_index}' does not exist" if cell == nil
+ cell.evaluate if cell
+ end
+
+ def self.cells(grid)
+ rows = grid.strip.split(/[\n]/)
+ rows.map! { |row| row.split(/\s{2,}|\t/) }
+ rows.map{ |row| row.delete_if { |cell| cell =~ /^\s*$/} }
+ rows
+ end
+
+ def to_s
+ sheet = @cells.map { |cell| cell.evaluate }
+ sheet = sheet.each_slice(@grid[0].size).to_a
+ sheet.map! { |row| row.join("\t") }
+ sheet.join("\n")
+ end
+
+ class Error < Exception
+ end
+
+ class Index
+ attr_accessor :column, :row
+
+ def self.column(number)
+ return "" if number == 0
+ number % 26 != 0 ? remainder = number % 26 : remainder = 26
+ number -= 26 if remainder == 26
+ number /= 26
+ return "#{ self.column(number) }" + "#{ self.to_letter(remainder) }"
+ end
+
+ def self.to_letter(number)
+ letters = [*"A".."Z"]
+ letter = letters.find { |letter| letter.ord - 64 == number }
+
+ return letter
+ end
+
+ def self.is_number?(number_string)
+ not number_string.match(/\A[-]?\d+?(\.\d+)?\Z/) == nil
+ end
+
+ def self.row(number)
+ number.to_s
+ end
+
+ def self.valid_index?(string)
+ string.match(/\A[A-Z]+[1-9]+\z/) != nil
+ end
+
+ def initialize(number, rows, columns)
+ @column = Index.column(number % columns + 1)
+ @row = Index.row(number / columns + 1)
+ end
+ end
+
+ class Expressions
+ attr_accessor :cell, :sheet, :expression
+ def initialize(expression, cell)
+ @cell = cell
+ @sheet = @cell.sheet
+ @expression = expression[1...expression.size]
+ end
+
+ def evaluate
+ return cell.sheet[find_key] if is_cell?
+ return Formula.beautify(find_key.to_f) if is_number?
+ raise Error, "Invalid expression '#{ expression }'" if not is_valid?
+ raise Error, "Unknown function '#{ find_key }'" if formula? == nil
+ Formula.new(sheet, arguments).method(formula?.downcase).call
+ end
+
+ def is_cell?
+ expression =~ (/\A *[A-Z]+[0-9]+ *\z/)
+ end
+
+ def is_number?
+ expression =~ (/\A *[-]?\d+?(\.\d+)?\Z/)
+ end
+ def is_valid?
+ (expression.match(%r{\A\ *[A-Z]+\ *\(\ *(([-]?\d+?(\.\d+)?)|
+ ([A-Z]+[0-9]+))?\ *\)\z}x) != nil) or \
+ (expression.match(%r{\A\ *[A-Z]+\ *\(\ *((([-]?\d+?(\.\d+)?)|
+ ([A-Z]+[0-9]+))\ *,\ *)+\ *(([-]?\d+?(\.\d+)?)
+ |([A-Z]+[0-9]+))\ *\)\ *\z}x) != nil)
+ end
+
+
+
+ def formula?
+ name = Formula.class_eval("@@names").
+ find { |formula| formula == find_key }
+ end
+ def arguments
+ arguments = expression.strip.split(/[()]/)[1].strip
+ arguments
+ end
+ def find_key
+ key = expression.match(/[A-Z]+[0-9]+/).to_s if is_cell?
+ key = expression.match(/[-]?\d+?(\.\d+)?/).to_s if is_number?
+ key = expression.match(/[A-Z]+/).to_s if not (is_cell? or is_number?) \
+ and is_valid?
+ key
+ end
+
+ end
+
+ class Cell
+ attr_accessor :content, :index, :sheet, :expression
+ def initialize(content, index, sheet)
+ @sheet = sheet
+ @content = content
+ @index = Index.new(index, sheet.grid.size, sheet.grid[0].size)
+ @expression = Expressions.new(content, self) if is_expression?
+ end
+
+ def is_expression?
+ content[0] == "="
+ end
+
+ def evaluate
+ return content if not is_expression?
+ @expression.evaluate
+ end
+
+ def index_string
+ @index.column + @index.row
+ end
+ end
+
+
+ class Formula
+ @@names = ["ADD", "MULTIPLY", "SUBTRACT", "DIVIDE", "MOD"]
+ attr_accessor :sheet, :arguments
+ def add
+ raise Error, "Wrong number of arguments for 'ADD': " \
+ "expected at least 2, got #{ arguments.size }" \
+ if arguments.size < 2
+ sum = arguments.map { |argument| argument.to_f }.reduce(:+)
+ Formula.beautify(sum)
+ end
+
+ def multiply
+ raise Error, "Wrong number of arguments for 'MULTIPLY': " \
+ "expected at least 2, got #{ arguments.size }" \
+ if arguments.size < 2
+ product = arguments.map { |argument| argument.to_f }.reduce(:*)
+ Formula.beautify(product)
+ end
+
+ def subtract
+ raise Error, "Wrong number of arguments for 'SUBTRACT': " \
+ "expected at least 2, got #{ arguments.size }" \
+ if arguments.size != 2
+ difference = arguments[0].to_f - arguments[1].to_f
+ Formula.beautify(difference)
+ end
+
+ def divide
+ raise Error, "Wrong number of arguments for 'DIVIDE': " \
+ "expected at least 2, got #{ arguments.size }" \
+ if arguments.size != 2
+ ratio = arguments[0].to_f / arguments[1].to_f
+ Formula.beautify(ratio)
+ end
+
+ def mod
+ raise Error, "Wrong number of arguments for 'MOD'" \
+ ": expected at least 2, got #{arguments.size}" \
+ if arguments.size != 2
+ remainder = arguments[0].to_f % arguments[1].to_f
+ Formula.beautify(remainder)
+ end
+
+ def initialize(sheet, arguments)
+ @sheet = sheet
+ @arguments = arguments.split(",").each { |argument| argument.strip! }
+ @arguments.map! do |argument|
+ Index.is_number?(argument) ? argument : sheet[argument]
+ end
+ end
+
+ def self.beautify(number)
+ return "#{ number.to_i }" if number == number.to_i
+ number_s = number.round(2).to_s.split(/[.]/)
+ number_s[1] = number_s[1] + "0" * (2 - number_s[1].size)
+ number_s.join(".")
+ end
+ end
+end

Георги обнови решението на 10.01.2016 22:51 (преди над 8 години)

class Spreadsheet
attr_accessor :grid, :cells
def initialize(grid = nil)
@grid = ""
@grid = Spreadsheet.cells(grid) if grid
@cells = @grid.flatten.each_with_index.
map {|cell, index| Cell.new(cell, index, self)} if grid
@cells.each {|cell| cell.sheet = self} if grid
end
def empty?
@grid.empty?
end
def cell_at(cell_index)
raise Error, "Invalid cell index '#{cell_index}'" \
if not Index.valid_index?(cell_index)
cell = @cells.find { |target| target.index_string == cell_index }
raise Error, "Cell '#{cell_index}' does not exist" if cell == nil
cell.content if cell
end
def [](cell_index)
raise Error, "Invalid cell index '#{cell_index}'" \
if not Index.valid_index?(cell_index)
cell = @cells.find { |target| target.index_string == cell_index }
raise Error, "Cell '#{cell_index}' does not exist" if cell == nil
cell.evaluate if cell
end
def self.cells(grid)
rows = grid.strip.split(/[\n]/)
rows.map! { |row| row.split(/\s{2,}|\t/) }
rows.map{ |row| row.delete_if { |cell| cell =~ /^\s*$/} }
rows
end
def to_s
sheet = @cells.map { |cell| cell.evaluate }
sheet = sheet.each_slice(@grid[0].size).to_a
sheet.map! { |row| row.join("\t") }
sheet.join("\n")
end
class Error < Exception
end
class Index
attr_accessor :column, :row
def self.column(number)
return "" if number == 0
number % 26 != 0 ? remainder = number % 26 : remainder = 26
number -= 26 if remainder == 26
number /= 26
return "#{ self.column(number) }" + "#{ self.to_letter(remainder) }"
end
def self.to_letter(number)
letters = [*"A".."Z"]
letter = letters.find { |letter| letter.ord - 64 == number }
return letter
end
def self.is_number?(number_string)
not number_string.match(/\A[-]?\d+?(\.\d+)?\Z/) == nil
end
def self.row(number)
number.to_s
end
def self.valid_index?(string)
string.match(/\A[A-Z]+[1-9]+\z/) != nil
end
def initialize(number, rows, columns)
@column = Index.column(number % columns + 1)
@row = Index.row(number / columns + 1)
end
end
class Expressions
attr_accessor :cell, :sheet, :expression
def initialize(expression, cell)
@cell = cell
@sheet = @cell.sheet
@expression = expression[1...expression.size]
end
def evaluate
- return cell.sheet[find_key] if is_cell?
+ return Formula.beautify_s(cell.sheet[find_key]) if is_cell?
return Formula.beautify(find_key.to_f) if is_number?
raise Error, "Invalid expression '#{ expression }'" if not is_valid?
raise Error, "Unknown function '#{ find_key }'" if formula? == nil
Formula.new(sheet, arguments).method(formula?.downcase).call
end
def is_cell?
expression =~ (/\A *[A-Z]+[0-9]+ *\z/)
end
def is_number?
expression =~ (/\A *[-]?\d+?(\.\d+)?\Z/)
end
def is_valid?
(expression.match(%r{\A\ *[A-Z]+\ *\(\ *(([-]?\d+?(\.\d+)?)|
([A-Z]+[0-9]+))?\ *\)\z}x) != nil) or \
(expression.match(%r{\A\ *[A-Z]+\ *\(\ *((([-]?\d+?(\.\d+)?)|
([A-Z]+[0-9]+))\ *,\ *)+\ *(([-]?\d+?(\.\d+)?)
|([A-Z]+[0-9]+))\ *\)\ *\z}x) != nil)
end
def formula?
name = Formula.class_eval("@@names").
find { |formula| formula == find_key }
end
def arguments
arguments = expression.strip.split(/[()]/)[1].strip
arguments
end
def find_key
key = expression.match(/[A-Z]+[0-9]+/).to_s if is_cell?
key = expression.match(/[-]?\d+?(\.\d+)?/).to_s if is_number?
key = expression.match(/[A-Z]+/).to_s if not (is_cell? or is_number?) \
and is_valid?
key
end
end
class Cell
attr_accessor :content, :index, :sheet, :expression
def initialize(content, index, sheet)
@sheet = sheet
@content = content
@index = Index.new(index, sheet.grid.size, sheet.grid[0].size)
@expression = Expressions.new(content, self) if is_expression?
end
def is_expression?
content[0] == "="
end
def evaluate
return content if not is_expression?
@expression.evaluate
end
def index_string
@index.column + @index.row
end
end
class Formula
@@names = ["ADD", "MULTIPLY", "SUBTRACT", "DIVIDE", "MOD"]
attr_accessor :sheet, :arguments
def add
raise Error, "Wrong number of arguments for 'ADD': " \
"expected at least 2, got #{ arguments.size }" \
if arguments.size < 2
sum = arguments.map { |argument| argument.to_f }.reduce(:+)
Formula.beautify(sum)
end
def multiply
raise Error, "Wrong number of arguments for 'MULTIPLY': " \
"expected at least 2, got #{ arguments.size }" \
if arguments.size < 2
product = arguments.map { |argument| argument.to_f }.reduce(:*)
Formula.beautify(product)
end
def subtract
raise Error, "Wrong number of arguments for 'SUBTRACT': " \
"expected at least 2, got #{ arguments.size }" \
if arguments.size != 2
difference = arguments[0].to_f - arguments[1].to_f
Formula.beautify(difference)
end
def divide
raise Error, "Wrong number of arguments for 'DIVIDE': " \
"expected at least 2, got #{ arguments.size }" \
if arguments.size != 2
ratio = arguments[0].to_f / arguments[1].to_f
Formula.beautify(ratio)
end
def mod
raise Error, "Wrong number of arguments for 'MOD'" \
": expected at least 2, got #{arguments.size}" \
if arguments.size != 2
remainder = arguments[0].to_f % arguments[1].to_f
Formula.beautify(remainder)
end
def initialize(sheet, arguments)
@sheet = sheet
@arguments = arguments.split(",").each { |argument| argument.strip! }
@arguments.map! do |argument|
Index.is_number?(argument) ? argument : sheet[argument]
end
end
def self.beautify(number)
return "#{ number.to_i }" if number == number.to_i
number_s = number.round(2).to_s.split(/[.]/)
number_s[1] = number_s[1] + "0" * (2 - number_s[1].size)
number_s.join(".")
end
+ def self.beautify_s(string)
+ output = string.strip
+ is_number = Index.is_number?(output)
+
+ is_number ? Formula.beautify(output.to_f) : output
+ end
end
-end
+end

Георги обнови решението на 11.01.2016 13:02 (преди над 8 години)

class Spreadsheet
attr_accessor :grid, :cells
def initialize(grid = nil)
@grid = ""
@grid = Spreadsheet.cells(grid) if grid
@cells = @grid.flatten.each_with_index.
map {|cell, index| Cell.new(cell, index, self)} if grid
@cells.each {|cell| cell.sheet = self} if grid
end
def empty?
@grid.empty?
end
def cell_at(cell_index)
raise Error, "Invalid cell index '#{cell_index}'" \
if not Index.valid_index?(cell_index)
cell = @cells.find { |target| target.index_string == cell_index }
raise Error, "Cell '#{cell_index}' does not exist" if cell == nil
cell.content if cell
end
def [](cell_index)
raise Error, "Invalid cell index '#{cell_index}'" \
if not Index.valid_index?(cell_index)
cell = @cells.find { |target| target.index_string == cell_index }
raise Error, "Cell '#{cell_index}' does not exist" if cell == nil
- cell.evaluate if cell
+ Formula.beautify_s(cell.evaluate) if cell
end
def self.cells(grid)
rows = grid.strip.split(/[\n]/)
rows.map! { |row| row.split(/\s{2,}|\t/) }
rows.map{ |row| row.delete_if { |cell| cell =~ /^\s*$/} }
rows
end
def to_s
sheet = @cells.map { |cell| cell.evaluate }
sheet = sheet.each_slice(@grid[0].size).to_a
sheet.map! { |row| row.join("\t") }
sheet.join("\n")
end
class Error < Exception
end
class Index
attr_accessor :column, :row
def self.column(number)
return "" if number == 0
number % 26 != 0 ? remainder = number % 26 : remainder = 26
number -= 26 if remainder == 26
number /= 26
return "#{ self.column(number) }" + "#{ self.to_letter(remainder) }"
end
def self.to_letter(number)
letters = [*"A".."Z"]
letter = letters.find { |letter| letter.ord - 64 == number }
return letter
end
def self.is_number?(number_string)
not number_string.match(/\A[-]?\d+?(\.\d+)?\Z/) == nil
end
def self.row(number)
number.to_s
end
def self.valid_index?(string)
string.match(/\A[A-Z]+[1-9]+\z/) != nil
end
def initialize(number, rows, columns)
@column = Index.column(number % columns + 1)
@row = Index.row(number / columns + 1)
end
end
class Expressions
attr_accessor :cell, :sheet, :expression
def initialize(expression, cell)
@cell = cell
@sheet = @cell.sheet
@expression = expression[1...expression.size]
end
def evaluate
return Formula.beautify_s(cell.sheet[find_key]) if is_cell?
return Formula.beautify(find_key.to_f) if is_number?
raise Error, "Invalid expression '#{ expression }'" if not is_valid?
raise Error, "Unknown function '#{ find_key }'" if formula? == nil
Formula.new(sheet, arguments).method(formula?.downcase).call
end
def is_cell?
expression =~ (/\A *[A-Z]+[0-9]+ *\z/)
end
def is_number?
expression =~ (/\A *[-]?\d+?(\.\d+)?\Z/)
end
def is_valid?
(expression.match(%r{\A\ *[A-Z]+\ *\(\ *(([-]?\d+?(\.\d+)?)|
([A-Z]+[0-9]+))?\ *\)\z}x) != nil) or \
(expression.match(%r{\A\ *[A-Z]+\ *\(\ *((([-]?\d+?(\.\d+)?)|
([A-Z]+[0-9]+))\ *,\ *)+\ *(([-]?\d+?(\.\d+)?)
|([A-Z]+[0-9]+))\ *\)\ *\z}x) != nil)
end
def formula?
name = Formula.class_eval("@@names").
find { |formula| formula == find_key }
end
def arguments
arguments = expression.strip.split(/[()]/)[1].strip
arguments
end
def find_key
key = expression.match(/[A-Z]+[0-9]+/).to_s if is_cell?
key = expression.match(/[-]?\d+?(\.\d+)?/).to_s if is_number?
key = expression.match(/[A-Z]+/).to_s if not (is_cell? or is_number?) \
and is_valid?
key
end
end
class Cell
attr_accessor :content, :index, :sheet, :expression
def initialize(content, index, sheet)
@sheet = sheet
@content = content
@index = Index.new(index, sheet.grid.size, sheet.grid[0].size)
@expression = Expressions.new(content, self) if is_expression?
end
def is_expression?
content[0] == "="
end
def evaluate
return content if not is_expression?
@expression.evaluate
end
def index_string
@index.column + @index.row
end
end
class Formula
@@names = ["ADD", "MULTIPLY", "SUBTRACT", "DIVIDE", "MOD"]
attr_accessor :sheet, :arguments
def add
raise Error, "Wrong number of arguments for 'ADD': " \
"expected at least 2, got #{ arguments.size }" \
if arguments.size < 2
sum = arguments.map { |argument| argument.to_f }.reduce(:+)
Formula.beautify(sum)
end
def multiply
raise Error, "Wrong number of arguments for 'MULTIPLY': " \
"expected at least 2, got #{ arguments.size }" \
if arguments.size < 2
product = arguments.map { |argument| argument.to_f }.reduce(:*)
Formula.beautify(product)
end
def subtract
raise Error, "Wrong number of arguments for 'SUBTRACT': " \
- "expected at least 2, got #{ arguments.size }" \
+ "expected 2, got #{ arguments.size }" \
if arguments.size != 2
difference = arguments[0].to_f - arguments[1].to_f
Formula.beautify(difference)
end
def divide
raise Error, "Wrong number of arguments for 'DIVIDE': " \
- "expected at least 2, got #{ arguments.size }" \
+ "expected 2, got #{ arguments.size }" \
if arguments.size != 2
ratio = arguments[0].to_f / arguments[1].to_f
Formula.beautify(ratio)
end
def mod
raise Error, "Wrong number of arguments for 'MOD'" \
- ": expected at least 2, got #{arguments.size}" \
+ ": expected 2, got #{ arguments.size }" \
if arguments.size != 2
remainder = arguments[0].to_f % arguments[1].to_f
Formula.beautify(remainder)
end
def initialize(sheet, arguments)
@sheet = sheet
@arguments = arguments.split(",").each { |argument| argument.strip! }
@arguments.map! do |argument|
Index.is_number?(argument) ? argument : sheet[argument]
end
end
def self.beautify(number)
return "#{ number.to_i }" if number == number.to_i
number_s = number.round(2).to_s.split(/[.]/)
number_s[1] = number_s[1] + "0" * (2 - number_s[1].size)
number_s.join(".")
end
def self.beautify_s(string)
output = string.strip
is_number = Index.is_number?(output)
is_number ? Formula.beautify(output.to_f) : output
end
end
end

Георги обнови решението на 11.01.2016 14:11 (преди над 8 години)

class Spreadsheet
attr_accessor :grid, :cells
def initialize(grid = nil)
@grid = ""
@grid = Spreadsheet.cells(grid) if grid
@cells = @grid.flatten.each_with_index.
map {|cell, index| Cell.new(cell, index, self)} if grid
@cells.each {|cell| cell.sheet = self} if grid
end
def empty?
@grid.empty?
end
def cell_at(cell_index)
raise Error, "Invalid cell index '#{cell_index}'" \
if not Index.valid_index?(cell_index)
cell = @cells.find { |target| target.index_string == cell_index }
raise Error, "Cell '#{cell_index}' does not exist" if cell == nil
cell.content if cell
end
def [](cell_index)
raise Error, "Invalid cell index '#{cell_index}'" \
if not Index.valid_index?(cell_index)
cell = @cells.find { |target| target.index_string == cell_index }
raise Error, "Cell '#{cell_index}' does not exist" if cell == nil
Formula.beautify_s(cell.evaluate) if cell
end
def self.cells(grid)
rows = grid.strip.split(/[\n]/)
rows.map! { |row| row.split(/\s{2,}|\t/) }
rows.map{ |row| row.delete_if { |cell| cell =~ /^\s*$/} }
rows
end
def to_s
sheet = @cells.map { |cell| cell.evaluate }
sheet = sheet.each_slice(@grid[0].size).to_a
sheet.map! { |row| row.join("\t") }
sheet.join("\n")
end
class Error < Exception
end
class Index
attr_accessor :column, :row
def self.column(number)
return "" if number == 0
number % 26 != 0 ? remainder = number % 26 : remainder = 26
number -= 26 if remainder == 26
number /= 26
return "#{ self.column(number) }" + "#{ self.to_letter(remainder) }"
end
def self.to_letter(number)
letters = [*"A".."Z"]
letter = letters.find { |letter| letter.ord - 64 == number }
return letter
end
def self.is_number?(number_string)
- not number_string.match(/\A[-]?\d+?(\.\d+)?\Z/) == nil
+ not number_string.match(/\A[-]?\d+(\.\d+)?\Z/) == nil
end
def self.row(number)
number.to_s
end
def self.valid_index?(string)
string.match(/\A[A-Z]+[1-9]+\z/) != nil
end
def initialize(number, rows, columns)
@column = Index.column(number % columns + 1)
@row = Index.row(number / columns + 1)
end
end
class Expressions
attr_accessor :cell, :sheet, :expression
def initialize(expression, cell)
@cell = cell
@sheet = @cell.sheet
- @expression = expression[1...expression.size]
+ @expression = expression[1...expression.size].strip
end
def evaluate
return Formula.beautify_s(cell.sheet[find_key]) if is_cell?
return Formula.beautify(find_key.to_f) if is_number?
raise Error, "Invalid expression '#{ expression }'" if not is_valid?
raise Error, "Unknown function '#{ find_key }'" if formula? == nil
Formula.new(sheet, arguments).method(formula?.downcase).call
end
def is_cell?
expression =~ (/\A *[A-Z]+[0-9]+ *\z/)
end
def is_number?
- expression =~ (/\A *[-]?\d+?(\.\d+)?\Z/)
+ expression =~ (/\A *[-]?\d+(\.\d+)?\Z/)
end
def is_valid?
- (expression.match(%r{\A\ *[A-Z]+\ *\(\ *(([-]?\d+?(\.\d+)?)|
- ([A-Z]+[0-9]+))?\ *\)\z}x) != nil) or \
- (expression.match(%r{\A\ *[A-Z]+\ *\(\ *((([-]?\d+?(\.\d+)?)|
- ([A-Z]+[0-9]+))\ *,\ *)+\ *(([-]?\d+?(\.\d+)?)
+ (expression.match(%r{\A\ *[A-Z]+\ *\(\ *(([-]?\d+(\.\d+)?)|
+ ([A-Z]+[0-9]+))?\ *\)\z}x) != nil) || \
+ (expression.match(%r{\A\ *[A-Z]+\ *\(\ *((([-]?\d+(\.\d+)?)|
+ ([A-Z]+[0-9]+))\ *,\ *)+\ *(([-]?\d+(\.\d+)?)
|([A-Z]+[0-9]+))\ *\)\ *\z}x) != nil)
end
def formula?
name = Formula.class_eval("@@names").
- find { |formula| formula == find_key }
+ find { |formula| formula == find_key }
end
def arguments
- arguments = expression.strip.split(/[()]/)[1].strip
+ arguments = expression.split(/[()]/)[1].strip
arguments
end
def find_key
key = expression.match(/[A-Z]+[0-9]+/).to_s if is_cell?
- key = expression.match(/[-]?\d+?(\.\d+)?/).to_s if is_number?
+ key = expression.match(/[-]?\d+(\.\d+)?/).to_s if is_number?
key = expression.match(/[A-Z]+/).to_s if not (is_cell? or is_number?) \
and is_valid?
key
end
end
class Cell
attr_accessor :content, :index, :sheet, :expression
def initialize(content, index, sheet)
@sheet = sheet
@content = content
@index = Index.new(index, sheet.grid.size, sheet.grid[0].size)
@expression = Expressions.new(content, self) if is_expression?
end
def is_expression?
content[0] == "="
end
def evaluate
return content if not is_expression?
@expression.evaluate
end
def index_string
@index.column + @index.row
end
end
class Formula
@@names = ["ADD", "MULTIPLY", "SUBTRACT", "DIVIDE", "MOD"]
attr_accessor :sheet, :arguments
def add
raise Error, "Wrong number of arguments for 'ADD': " \
"expected at least 2, got #{ arguments.size }" \
if arguments.size < 2
sum = arguments.map { |argument| argument.to_f }.reduce(:+)
Formula.beautify(sum)
end
def multiply
raise Error, "Wrong number of arguments for 'MULTIPLY': " \
"expected at least 2, got #{ arguments.size }" \
if arguments.size < 2
product = arguments.map { |argument| argument.to_f }.reduce(:*)
Formula.beautify(product)
end
def subtract
raise Error, "Wrong number of arguments for 'SUBTRACT': " \
"expected 2, got #{ arguments.size }" \
if arguments.size != 2
difference = arguments[0].to_f - arguments[1].to_f
Formula.beautify(difference)
end
def divide
raise Error, "Wrong number of arguments for 'DIVIDE': " \
"expected 2, got #{ arguments.size }" \
if arguments.size != 2
ratio = arguments[0].to_f / arguments[1].to_f
Formula.beautify(ratio)
end
def mod
raise Error, "Wrong number of arguments for 'MOD'" \
- ": expected 2, got #{ arguments.size }" \
+ ": expected 2, got #{arguments.size}" \
if arguments.size != 2
remainder = arguments[0].to_f % arguments[1].to_f
Formula.beautify(remainder)
end
def initialize(sheet, arguments)
@sheet = sheet
@arguments = arguments.split(",").each { |argument| argument.strip! }
@arguments.map! do |argument|
Index.is_number?(argument) ? argument : sheet[argument]
end
end
def self.beautify(number)
- return "#{ number.to_i }" if number == number.to_i
- number_s = number.round(2).to_s.split(/[.]/)
+ number_s = number.round(2)
+ return "#{ number_s.to_i }" if number_s == number_s.to_i
+ number_s = number_s.to_s.split(/[.]/)
number_s[1] = number_s[1] + "0" * (2 - number_s[1].size)
number_s.join(".")
end
def self.beautify_s(string)
output = string.strip
is_number = Index.is_number?(output)
is_number ? Formula.beautify(output.to_f) : output
end
end
end

Георги обнови решението на 11.01.2016 14:19 (преди над 8 години)

class Spreadsheet
attr_accessor :grid, :cells
def initialize(grid = nil)
@grid = ""
@grid = Spreadsheet.cells(grid) if grid
@cells = @grid.flatten.each_with_index.
- map {|cell, index| Cell.new(cell, index, self)} if grid
+ map { |cell, index| Cell.new(cell, index, self) } if grid
@cells.each {|cell| cell.sheet = self} if grid
end
def empty?
@grid.empty?
end
def cell_at(cell_index)
- raise Error, "Invalid cell index '#{cell_index}'" \
+ raise Error, "Invalid cell index '#{ cell_index }'" \
if not Index.valid_index?(cell_index)
cell = @cells.find { |target| target.index_string == cell_index }
- raise Error, "Cell '#{cell_index}' does not exist" if cell == nil
+ raise Error, "Cell '#{ cell_index }' does not exist" if cell == nil
cell.content if cell
end
def [](cell_index)
- raise Error, "Invalid cell index '#{cell_index}'" \
+ raise Error, "Invalid cell index '#{ cell_index }'" \
if not Index.valid_index?(cell_index)
cell = @cells.find { |target| target.index_string == cell_index }
- raise Error, "Cell '#{cell_index}' does not exist" if cell == nil
+ raise Error, "Cell '#{ cell_index }' does not exist" if cell == nil
Formula.beautify_s(cell.evaluate) if cell
end
def self.cells(grid)
rows = grid.strip.split(/[\n]/)
rows.map! { |row| row.split(/\s{2,}|\t/) }
- rows.map{ |row| row.delete_if { |cell| cell =~ /^\s*$/} }
+ rows.map{ |row| row.delete_if { |cell| cell =~ /^\s*$/ } }
rows
end
def to_s
sheet = @cells.map { |cell| cell.evaluate }
sheet = sheet.each_slice(@grid[0].size).to_a
sheet.map! { |row| row.join("\t") }
sheet.join("\n")
end
class Error < Exception
end
class Index
attr_accessor :column, :row
def self.column(number)
return "" if number == 0
number % 26 != 0 ? remainder = number % 26 : remainder = 26
number -= 26 if remainder == 26
number /= 26
return "#{ self.column(number) }" + "#{ self.to_letter(remainder) }"
end
def self.to_letter(number)
letters = [*"A".."Z"]
letter = letters.find { |letter| letter.ord - 64 == number }
return letter
end
def self.is_number?(number_string)
not number_string.match(/\A[-]?\d+(\.\d+)?\Z/) == nil
end
def self.row(number)
number.to_s
end
def self.valid_index?(string)
string.match(/\A[A-Z]+[1-9]+\z/) != nil
end
def initialize(number, rows, columns)
@column = Index.column(number % columns + 1)
@row = Index.row(number / columns + 1)
end
end
class Expressions
attr_accessor :cell, :sheet, :expression
def initialize(expression, cell)
@cell = cell
@sheet = @cell.sheet
@expression = expression[1...expression.size].strip
end
def evaluate
return Formula.beautify_s(cell.sheet[find_key]) if is_cell?
return Formula.beautify(find_key.to_f) if is_number?
raise Error, "Invalid expression '#{ expression }'" if not is_valid?
raise Error, "Unknown function '#{ find_key }'" if formula? == nil
Formula.new(sheet, arguments).method(formula?.downcase).call
end
def is_cell?
expression =~ (/\A *[A-Z]+[0-9]+ *\z/)
end
def is_number?
expression =~ (/\A *[-]?\d+(\.\d+)?\Z/)
end
def is_valid?
(expression.match(%r{\A\ *[A-Z]+\ *\(\ *(([-]?\d+(\.\d+)?)|
([A-Z]+[0-9]+))?\ *\)\z}x) != nil) || \
(expression.match(%r{\A\ *[A-Z]+\ *\(\ *((([-]?\d+(\.\d+)?)|
([A-Z]+[0-9]+))\ *,\ *)+\ *(([-]?\d+(\.\d+)?)
|([A-Z]+[0-9]+))\ *\)\ *\z}x) != nil)
end
def formula?
name = Formula.class_eval("@@names").
find { |formula| formula == find_key }
end
def arguments
arguments = expression.split(/[()]/)[1].strip
arguments
end
def find_key
key = expression.match(/[A-Z]+[0-9]+/).to_s if is_cell?
key = expression.match(/[-]?\d+(\.\d+)?/).to_s if is_number?
key = expression.match(/[A-Z]+/).to_s if not (is_cell? or is_number?) \
and is_valid?
key
end
end
class Cell
attr_accessor :content, :index, :sheet, :expression
def initialize(content, index, sheet)
@sheet = sheet
@content = content
@index = Index.new(index, sheet.grid.size, sheet.grid[0].size)
@expression = Expressions.new(content, self) if is_expression?
end
def is_expression?
content[0] == "="
end
def evaluate
return content if not is_expression?
@expression.evaluate
end
def index_string
@index.column + @index.row
end
end
class Formula
@@names = ["ADD", "MULTIPLY", "SUBTRACT", "DIVIDE", "MOD"]
attr_accessor :sheet, :arguments
def add
raise Error, "Wrong number of arguments for 'ADD': " \
"expected at least 2, got #{ arguments.size }" \
if arguments.size < 2
sum = arguments.map { |argument| argument.to_f }.reduce(:+)
Formula.beautify(sum)
end
def multiply
raise Error, "Wrong number of arguments for 'MULTIPLY': " \
"expected at least 2, got #{ arguments.size }" \
if arguments.size < 2
product = arguments.map { |argument| argument.to_f }.reduce(:*)
Formula.beautify(product)
end
def subtract
raise Error, "Wrong number of arguments for 'SUBTRACT': " \
"expected 2, got #{ arguments.size }" \
if arguments.size != 2
difference = arguments[0].to_f - arguments[1].to_f
Formula.beautify(difference)
end
def divide
raise Error, "Wrong number of arguments for 'DIVIDE': " \
"expected 2, got #{ arguments.size }" \
if arguments.size != 2
ratio = arguments[0].to_f / arguments[1].to_f
Formula.beautify(ratio)
end
def mod
raise Error, "Wrong number of arguments for 'MOD'" \
": expected 2, got #{arguments.size}" \
if arguments.size != 2
remainder = arguments[0].to_f % arguments[1].to_f
Formula.beautify(remainder)
end
def initialize(sheet, arguments)
@sheet = sheet
@arguments = arguments.split(",").each { |argument| argument.strip! }
@arguments.map! do |argument|
Index.is_number?(argument) ? argument : sheet[argument]
end
end
def self.beautify(number)
number_s = number.round(2)
return "#{ number_s.to_i }" if number_s == number_s.to_i
number_s = number_s.to_s.split(/[.]/)
number_s[1] = number_s[1] + "0" * (2 - number_s[1].size)
number_s.join(".")
end
def self.beautify_s(string)
output = string.strip
is_number = Index.is_number?(output)
is_number ? Formula.beautify(output.to_f) : output
end
end
end

Георги обнови решението на 11.01.2016 14:34 (преди над 8 години)

class Spreadsheet
attr_accessor :grid, :cells
def initialize(grid = nil)
@grid = ""
@grid = Spreadsheet.cells(grid) if grid
@cells = @grid.flatten.each_with_index.
map { |cell, index| Cell.new(cell, index, self) } if grid
@cells.each {|cell| cell.sheet = self} if grid
end
def empty?
@grid.empty?
end
def cell_at(cell_index)
raise Error, "Invalid cell index '#{ cell_index }'" \
if not Index.valid_index?(cell_index)
cell = @cells.find { |target| target.index_string == cell_index }
raise Error, "Cell '#{ cell_index }' does not exist" if cell == nil
cell.content if cell
end
def [](cell_index)
raise Error, "Invalid cell index '#{ cell_index }'" \
if not Index.valid_index?(cell_index)
cell = @cells.find { |target| target.index_string == cell_index }
raise Error, "Cell '#{ cell_index }' does not exist" if cell == nil
Formula.beautify_s(cell.evaluate) if cell
end
def self.cells(grid)
rows = grid.strip.split(/[\n]/)
rows.map! { |row| row.split(/\s{2,}|\t/) }
rows.map{ |row| row.delete_if { |cell| cell =~ /^\s*$/ } }
rows
end
def to_s
sheet = @cells.map { |cell| cell.evaluate }
sheet = sheet.each_slice(@grid[0].size).to_a
sheet.map! { |row| row.join("\t") }
sheet.join("\n")
end
class Error < Exception
end
class Index
attr_accessor :column, :row
def self.column(number)
return "" if number == 0
number % 26 != 0 ? remainder = number % 26 : remainder = 26
number -= 26 if remainder == 26
number /= 26
return "#{ self.column(number) }" + "#{ self.to_letter(remainder) }"
end
def self.to_letter(number)
letters = [*"A".."Z"]
letter = letters.find { |letter| letter.ord - 64 == number }
return letter
end
def self.is_number?(number_string)
not number_string.match(/\A[-]?\d+(\.\d+)?\Z/) == nil
end
def self.row(number)
number.to_s
end
def self.valid_index?(string)
string.match(/\A[A-Z]+[1-9]+\z/) != nil
end
def initialize(number, rows, columns)
@column = Index.column(number % columns + 1)
@row = Index.row(number / columns + 1)
end
end
class Expressions
attr_accessor :cell, :sheet, :expression
def initialize(expression, cell)
@cell = cell
@sheet = @cell.sheet
@expression = expression[1...expression.size].strip
end
def evaluate
return Formula.beautify_s(cell.sheet[find_key]) if is_cell?
return Formula.beautify(find_key.to_f) if is_number?
raise Error, "Invalid expression '#{ expression }'" if not is_valid?
raise Error, "Unknown function '#{ find_key }'" if formula? == nil
Formula.new(sheet, arguments).method(formula?.downcase).call
end
def is_cell?
expression =~ (/\A *[A-Z]+[0-9]+ *\z/)
end
def is_number?
expression =~ (/\A *[-]?\d+(\.\d+)?\Z/)
end
def is_valid?
(expression.match(%r{\A\ *[A-Z]+\ *\(\ *(([-]?\d+(\.\d+)?)|
([A-Z]+[0-9]+))?\ *\)\z}x) != nil) || \
(expression.match(%r{\A\ *[A-Z]+\ *\(\ *((([-]?\d+(\.\d+)?)|
([A-Z]+[0-9]+))\ *,\ *)+\ *(([-]?\d+(\.\d+)?)
|([A-Z]+[0-9]+))\ *\)\ *\z}x) != nil)
end
def formula?
name = Formula.class_eval("@@names").
find { |formula| formula == find_key }
end
def arguments
arguments = expression.split(/[()]/)[1].strip
arguments
end
def find_key
key = expression.match(/[A-Z]+[0-9]+/).to_s if is_cell?
key = expression.match(/[-]?\d+(\.\d+)?/).to_s if is_number?
key = expression.match(/[A-Z]+/).to_s if not (is_cell? or is_number?) \
and is_valid?
key
end
end
class Cell
attr_accessor :content, :index, :sheet, :expression
def initialize(content, index, sheet)
@sheet = sheet
@content = content
@index = Index.new(index, sheet.grid.size, sheet.grid[0].size)
@expression = Expressions.new(content, self) if is_expression?
end
def is_expression?
content[0] == "="
end
def evaluate
return content if not is_expression?
@expression.evaluate
end
def index_string
@index.column + @index.row
end
end
class Formula
@@names = ["ADD", "MULTIPLY", "SUBTRACT", "DIVIDE", "MOD"]
attr_accessor :sheet, :arguments
def add
raise Error, "Wrong number of arguments for 'ADD': " \
"expected at least 2, got #{ arguments.size }" \
- if arguments.size < 2
+ if arguments.size < 2
sum = arguments.map { |argument| argument.to_f }.reduce(:+)
Formula.beautify(sum)
end
def multiply
raise Error, "Wrong number of arguments for 'MULTIPLY': " \
"expected at least 2, got #{ arguments.size }" \
- if arguments.size < 2
+ if arguments.size < 2
product = arguments.map { |argument| argument.to_f }.reduce(:*)
Formula.beautify(product)
end
def subtract
raise Error, "Wrong number of arguments for 'SUBTRACT': " \
"expected 2, got #{ arguments.size }" \
- if arguments.size != 2
+ if arguments.size != 2
difference = arguments[0].to_f - arguments[1].to_f
Formula.beautify(difference)
end
def divide
raise Error, "Wrong number of arguments for 'DIVIDE': " \
"expected 2, got #{ arguments.size }" \
- if arguments.size != 2
+ if arguments.size != 2
ratio = arguments[0].to_f / arguments[1].to_f
Formula.beautify(ratio)
end
def mod
raise Error, "Wrong number of arguments for 'MOD'" \
- ": expected 2, got #{arguments.size}" \
- if arguments.size != 2
+ ": expected 2, got #{arguments.size}" \
+ if arguments.size != 2
remainder = arguments[0].to_f % arguments[1].to_f
Formula.beautify(remainder)
end
def initialize(sheet, arguments)
@sheet = sheet
@arguments = arguments.split(",").each { |argument| argument.strip! }
@arguments.map! do |argument|
Index.is_number?(argument) ? argument : sheet[argument]
end
end
def self.beautify(number)
number_s = number.round(2)
return "#{ number_s.to_i }" if number_s == number_s.to_i
number_s = number_s.to_s.split(/[.]/)
number_s[1] = number_s[1] + "0" * (2 - number_s[1].size)
number_s.join(".")
end
def self.beautify_s(string)
output = string.strip
is_number = Index.is_number?(output)
is_number ? Formula.beautify(output.to_f) : output
end
end
end