Решение на Осма задача от Ивайло Чернев

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

Към профила на Ивайло Чернев

Резултати

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

Код

class Spreadsheet
class Error < StandardError
def initialize(message)
super
end
end
attr_accessor :table
def initialize(text = nil)
if text == nil
@table = []
else
@table = fetch_table(text)
end
end
def empty?
@table == []
end
def cell_at(string)
index = TableIndex.new string
row, column = index.integer_indexes[0], index.integer_indexes[1]
begin
@table[row][column]
rescue StandardError
raise Error, "Cell '#{string}' does not exist"
end
end
def [](string)
result = self.calculate(string).to_s
number = result.match(/\A([0-9.]+)\Z/)
return result if number == nil or cell_at(string).match(/\A=/) == nil
return number[1].to_i.to_s if number[1].to_i == Float(number[1])
"%.2f" % number[1]
end
def calculate(string)
cell = cell_at(string)
if cell[0] == '='
formula_cell cell
else
cell
end
end
def to_s
table = @table.map.with_index do |row, i|
row.map.with_index { |cell, j| self[TableIndex.get_string_index(i, j)]}
end
table = table.map { |row| row.join("\t") }
table.join("\n")
end
class TableIndex
attr_accessor :integer_indexes
def initialize(string)
@table_indexes = fetch_indexes(string)
@integer_indexes = get_integer_indexes
end
def get_integer_indexes
indexes = @table_indexes
column = integer_column_index indexes[0]
row = indexes[1].to_i
[row - 1, column - 1]
end
def integer_column_index(string)
offset = 'A'.ord - 1
string.chars.inject(0){ |x,c| x * 26 + c.ord - offset }
end
def fetch_indexes(string)
match = string.match(/\A([A-Z]+)([1-9][0-9]*)\Z/)
if match != nil
[match[1], match[2]]
else
raise Spreadsheet::Error, "Ivalid cell index '#{string}'"
end
end
def self.get_string_index(x, y)
x = x + 1
hash = Hash.new { |hash,key| hash[key] = hash[key - 1].next }
hash = hash.merge({0 => "A"})
hash[y] + x.to_s
end
end
class Formula
FORMULAS = ["ADD", "MULTIPLY", "SUBTRACT", "DIVIDE", "MOD"]
LESS = "Wrong number of arguments for 'FOO': expected at least %s, got $s"
WRONG = "Wrong number of arguments for 'FOO': expected %s, got %s"
FLOAT_FORMAT = "%.2f"
def initialize(table, string)
@table = table
@string = string
end
def evaluate
formula = @string.match(/\A([A-Z]+)\([0-9A-Z.]+(, [0-9A-Z.]+)*\)\Z/)
raise Error, "Invalid expression '#{@string}'" if formula == nil
unless FORMULAS.include? formula[1]
raise Error, "Unknown function '#{formula[1]}'"
else
self.send formula[1].downcase.to_sym
end
end
def fetch_arguments
formula = @string.match(/\([0-9A-Z.]+(, [0-9A-Z.]+)*\)\Z/)
arguments = formula[0][1..-2].split(',').map(&:strip)
arguments = arguments.map do |argument|
if argument.match(/[A-Z]/) != nil
Float(@table.calculate(argument))
else
Float(argument)
end
end
end
def add
arguments = fetch_arguments
raise Error, LESS % [2, arguments.size] if arguments.size < 2
answer = arguments.inject(:+)
end
def multiply
arguments = fetch_arguments
raise Error, LESS % [2, arguments.size] if arguments.size < 2
answer = arguments.inject(:*)
end
def subtract
arguments = fetch_arguments
raise Error, WRONG % [2, arguments.size] if arguments.size != 2
answer = arguments[0] - arguments[1]
end
def divide
arguments = fetch_arguments
raise Error, WRONG % [2, arguments.size] if arguments.size != 2
answer = arguments[0] / arguments[1]
end
def mod
arguments = fetch_arguments
raise Error, WRONG % [2, arguments.size] if arguments.size != 2
answer = arguments[0] % arguments[1]
end
end
private
def fetch_table(text)
table = []
rows = text.split("\n")
rows = rows.map { |e| e.strip }.select { |e| e != "" }
rows.each { |row| table << row.split(/ {2,}|\t/).map(&:strip) }
table
end
def formula_cell(cell)
number = cell.match(/= *([0-9.]+)\Z/)
return number[1] if number != nil
is_reference = cell.match(/\A=([A-Z]+)([1-9][0-9]*)\Z/)
return self[is_reference[0][1 .. -1]] if is_reference != nil
Formula.new(self, cell[1..-1]).evaluate
end
end

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

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

Failures:

  1) Spreadsheet#[] raises an exception for less than two arguments passed to ADD
     Failure/Error: expect { Spreadsheet.new('=ADD(1)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'ADD': expected at least 2, got 1/, got #<Spreadsheet::Error: Wrong number of arguments for 'FOO': expected at least 2, got $s> with backtrace:
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:130:in `add'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:112:in `evaluate'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:174:in `formula_cell'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:44:in `calculate'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:34:in `[]'
         # /tmp/d20160121-5693-1tc0wdf/spec.rb:119:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1tc0wdf/spec.rb:119: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-1tc0wdf/spec.rb:119: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#[] raises an exception for less than two arguments to MULTIPLY
     Failure/Error: expect { Spreadsheet.new('=MULTIPLY(1)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'MULTIPLY': expected at least 2, got 1/, got #<Spreadsheet::Error: Wrong number of arguments for 'FOO': expected at least 2, got $s> with backtrace:
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:136:in `multiply'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:112:in `evaluate'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:174:in `formula_cell'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:44:in `calculate'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:34:in `[]'
         # /tmp/d20160121-5693-1tc0wdf/spec.rb:149:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1tc0wdf/spec.rb:149: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-1tc0wdf/spec.rb:149: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 when SUBTRACT is called with a wrong number of arguments
     Failure/Error: expect { Spreadsheet.new('=SUBTRACT(1)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'SUBTRACT': expected 2, got 1/, got #<Spreadsheet::Error: Wrong number of arguments for 'FOO': expected 2, got 1> with backtrace:
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:142:in `subtract'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:112:in `evaluate'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:174:in `formula_cell'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:44:in `calculate'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:34:in `[]'
         # /tmp/d20160121-5693-1tc0wdf/spec.rb:171:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1tc0wdf/spec.rb:171: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-1tc0wdf/spec.rb:171: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 when DIVIDE is called with a wrong number of arguments
     Failure/Error: expect { Spreadsheet.new('=DIVIDE(1)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'DIVIDE': expected 2, got 1/, got #<Spreadsheet::Error: Wrong number of arguments for 'FOO': expected 2, got 1> with backtrace:
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:148:in `divide'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:112:in `evaluate'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:174:in `formula_cell'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:44:in `calculate'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:34:in `[]'
         # /tmp/d20160121-5693-1tc0wdf/spec.rb:195:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1tc0wdf/spec.rb:195: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-1tc0wdf/spec.rb:195: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 when MOD is called with a wrong number of arguments
     Failure/Error: expect { Spreadsheet.new('=MOD(1)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'MOD': expected 2, got 1/, got #<Spreadsheet::Error: Wrong number of arguments for 'FOO': expected 2, got 1> with backtrace:
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:154:in `mod'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:112:in `evaluate'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:174:in `formula_cell'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:44:in `calculate'
         # /tmp/d20160121-5693-1tc0wdf/solution.rb:34:in `[]'
         # /tmp/d20160121-5693-1tc0wdf/spec.rb:219:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1tc0wdf/spec.rb:219: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-1tc0wdf/spec.rb:219: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.03483 seconds
40 examples, 5 failures

Failed examples:

rspec /tmp/d20160121-5693-1tc0wdf/spec.rb:118 # Spreadsheet#[] raises an exception for less than two arguments passed to ADD
rspec /tmp/d20160121-5693-1tc0wdf/spec.rb:148 # Spreadsheet#[] raises an exception for less than two arguments to MULTIPLY
rspec /tmp/d20160121-5693-1tc0wdf/spec.rb:170 # Spreadsheet#[] raises an exception when SUBTRACT is called with a wrong number of arguments
rspec /tmp/d20160121-5693-1tc0wdf/spec.rb:194 # Spreadsheet#[] raises an exception when DIVIDE is called with a wrong number of arguments
rspec /tmp/d20160121-5693-1tc0wdf/spec.rb:218 # Spreadsheet#[] raises an exception when MOD is called with a wrong number of arguments

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

Ивайло обнови решението на 11.01.2016 01:05 (преди над 8 години)

+class Spreadsheet
+
+ class Error < StandardError
+ def initialize(message)
+ super
+ end
+ end
+
+ attr_accessor :table
+
+ def initialize(text = nil)
+ if text == nil
+ @table = []
+ else
+ @table = fetch_table(text)
+ end
+ end
+
+ def empty?
+ @table == []
+ end
+
+ def cell_at(string)
+ index = TableIndex.new string
+ row, column = index.integer_indexes[0], index.integer_indexes[1]
+ begin
+ @table[row][column]
+ rescue StandardError
+ raise Error, "Cell '#{string}' does not exist"
+ end
+ end
+
+ def [](string)
+ cell = cell_at(string)
+ if cell[0] == '='
+ formula_cell cell
+ else
+ cell
+ end
+ end
+
+ def to_s
+ table = @table.map.with_index do |row, i|
+ row.map.with_index { |cell, j| self[TableIndex.get_string_index(i, j)]}
+ end
+ table = table.map { |row| row.join("\t") }
+ table.join("\n")
+
+ end
+
+ class TableIndex
+ attr_accessor :integer_indexes
+ def initialize(string)
+ @table_indexes = fetch_indexes(string)
+ @integer_indexes = get_integer_indexes
+ end
+
+ def get_integer_indexes
+ indexes = @table_indexes
+ column = integer_column_index indexes[0]
+ row = indexes[1].to_i
+ [row - 1, column - 1]
+ end
+
+ def integer_column_index(string)
+ offset = 'A'.ord - 1
+ string.chars.inject(0){ |x,c| x * 26 + c.ord - offset }
+ end
+
+ def fetch_indexes(string)
+ match = string.match(/\A([A-Z]+)([1-9][0-9]*)\Z/)
+ if match != nil
+ [match[1], match[2]]
+ else
+ raise Spreadsheet::Error, "Ivalid cell index '#{string}'"
+ end
+ end
+
+ def self.get_string_index(x, y)
+ x = x + 1
+ hash = Hash.new { |hash,key| hash[key] = hash[key - 1].next }
+ hash = hash.merge({0 => "A"})
+ hash[y] + x.to_s
+ end
+ end
+
+ class Formula
+ FORMULAS = ["ADD", "MULTIPLY", "SUBTRACT", "DIVIDE", "MOD"]
+ LESS = "Wrong number of arguments for 'FOO': expected at least %s, got $s"
+ WRONG = "Wrong number of arguments for 'FOO': expected %s, got %s"
+ FLOAT_FORMAT = "%.2f"
+
+ def initialize(table, string)
+ @table = table
+ @string = string
+ end
+
+ def evaluate
+ formula = @string.match(/\A([A-Z]+)\([0-9A-Z.]+(, [0-9A-Z.]+)*\)\Z/)
+ raise Error, "Invalid expression '#{@string}'" if formula == nil
+ unless FORMULAS.include? formula[1]
+ raise Error, "Unknown function '#{formula[1]}'"
+ else
+ self.send formula[1].downcase.to_sym
+ end
+ end
+
+ def fetch_arguments
+ formula = @string.match(/\([0-9A-Z.]+(, [0-9A-Z.]+)*\)\Z/)
+ arguments = formula[0][1..-2].split(',').map(&:strip)
+ arguments = arguments.map do |argument|
+ if argument.match(/[A-Z]/) != nil
+ Float(@table[argument])
+ else
+ Float(argument)
+ end
+ end
+ end
+
+ def add
+ arguments = fetch_arguments
+ raise Error, LESS % [2, arguments.size] if arguments.size < 2
+ answer = arguments.inject(:+)
+ return answer.to_i.to_s if answer.to_i == answer
+ FLOAT_FORMAT % answer
+ end
+
+ def multiply
+ arguments = fetch_arguments
+ raise Error, LESS % [2, arguments.size] if arguments.size < 2
+ answer = arguments.inject(:*)
+ return answer.to_i.to_s if answer.to_i == answer
+ FLOAT_FORMAT % answer
+ end
+
+ def subtract
+ arguments = fetch_arguments
+ raise Error, WRONG % [2, arguments.size] if arguments.size != 2
+ answer = arguments[0] - arguments[1]
+ return answer.to_i.to_s if answer.to_i == answer
+ FLOAT_FORMAT % answer
+ end
+
+ def divide
+ arguments = fetch_arguments
+ raise Error, WRONG % [2, arguments.size] if arguments.size != 2
+ answer = arguments[0] / arguments[1]
+ return answer.to_i.to_s if answer.to_i == answer
+ FLOAT_FORMAT % answer
+ end
+
+ def mod
+ arguments = fetch_arguments
+ raise Error, WRONG % [2, arguments.size] if arguments.size != 2
+ answer = arguments[0] % arguments[1]
+ return answer.to_i.to_s if answer.to_i == answer
+ FLOAT_FORMAT % answer
+ end
+ end
+
+ private
+
+ def fetch_table(text)
+ table = []
+ rows = text.split("\n")
+ rows = rows.map { |e| e.strip }.select { |e| e != "" }
+ rows.each { |row| table << fetch_row(row) }
+ table
+ end
+
+ def fetch_row(string)
+ row = []
+ string = string.split(/ {2,}|\t/)
+ string.map { |e| e.strip }.each { |cell| row << cell }
+ row
+ end
+
+ def formula_cell(cell)
+ number = cell.match(/= *([0-9.]+)/)
+ return number[1].to_i.to_s if number != nil and number[1].to_i == number[1]
+ return "%.2f" % number[1] if number != nil
+ is_reference = cell.match(/\A=([A-Z]+)([1-9][0-9]*)\Z/)
+ return self[is_reference[0][1 .. -1]] if is_reference != nil
+ Formula.new(self, cell[1..-1]).evaluate
+ end
+
+end

Ивайло обнови решението на 11.01.2016 14:33 (преди над 8 години)

class Spreadsheet
class Error < StandardError
def initialize(message)
super
end
end
attr_accessor :table
def initialize(text = nil)
if text == nil
@table = []
else
@table = fetch_table(text)
end
end
def empty?
@table == []
end
def cell_at(string)
index = TableIndex.new string
row, column = index.integer_indexes[0], index.integer_indexes[1]
begin
@table[row][column]
rescue StandardError
raise Error, "Cell '#{string}' does not exist"
end
end
def [](string)
+ result = self.calculate(string).to_s
+ number = result.match(/\A([0-9.]+)\Z/)
+ return result if number == nil or cell_at(string).match(/\A=/) == nil
+ return number[1].to_i.to_s if number[1].to_i == Float(number[1])
+ "%.2f" % number[1]
+ end
+
+ def calculate(string)
cell = cell_at(string)
if cell[0] == '='
formula_cell cell
else
cell
end
end
def to_s
table = @table.map.with_index do |row, i|
row.map.with_index { |cell, j| self[TableIndex.get_string_index(i, j)]}
end
table = table.map { |row| row.join("\t") }
table.join("\n")
end
class TableIndex
attr_accessor :integer_indexes
def initialize(string)
@table_indexes = fetch_indexes(string)
@integer_indexes = get_integer_indexes
end
def get_integer_indexes
indexes = @table_indexes
column = integer_column_index indexes[0]
row = indexes[1].to_i
[row - 1, column - 1]
end
def integer_column_index(string)
offset = 'A'.ord - 1
string.chars.inject(0){ |x,c| x * 26 + c.ord - offset }
end
def fetch_indexes(string)
match = string.match(/\A([A-Z]+)([1-9][0-9]*)\Z/)
if match != nil
[match[1], match[2]]
else
raise Spreadsheet::Error, "Ivalid cell index '#{string}'"
end
end
def self.get_string_index(x, y)
x = x + 1
hash = Hash.new { |hash,key| hash[key] = hash[key - 1].next }
hash = hash.merge({0 => "A"})
hash[y] + x.to_s
end
end
class Formula
FORMULAS = ["ADD", "MULTIPLY", "SUBTRACT", "DIVIDE", "MOD"]
LESS = "Wrong number of arguments for 'FOO': expected at least %s, got $s"
WRONG = "Wrong number of arguments for 'FOO': expected %s, got %s"
FLOAT_FORMAT = "%.2f"
def initialize(table, string)
@table = table
@string = string
end
def evaluate
formula = @string.match(/\A([A-Z]+)\([0-9A-Z.]+(, [0-9A-Z.]+)*\)\Z/)
raise Error, "Invalid expression '#{@string}'" if formula == nil
unless FORMULAS.include? formula[1]
raise Error, "Unknown function '#{formula[1]}'"
else
self.send formula[1].downcase.to_sym
end
end
def fetch_arguments
formula = @string.match(/\([0-9A-Z.]+(, [0-9A-Z.]+)*\)\Z/)
arguments = formula[0][1..-2].split(',').map(&:strip)
arguments = arguments.map do |argument|
if argument.match(/[A-Z]/) != nil
- Float(@table[argument])
+ Float(@table.calculate(argument))
else
Float(argument)
end
end
end
def add
arguments = fetch_arguments
raise Error, LESS % [2, arguments.size] if arguments.size < 2
answer = arguments.inject(:+)
- return answer.to_i.to_s if answer.to_i == answer
- FLOAT_FORMAT % answer
end
def multiply
arguments = fetch_arguments
raise Error, LESS % [2, arguments.size] if arguments.size < 2
answer = arguments.inject(:*)
- return answer.to_i.to_s if answer.to_i == answer
- FLOAT_FORMAT % answer
end
def subtract
arguments = fetch_arguments
raise Error, WRONG % [2, arguments.size] if arguments.size != 2
answer = arguments[0] - arguments[1]
- return answer.to_i.to_s if answer.to_i == answer
- FLOAT_FORMAT % answer
end
def divide
arguments = fetch_arguments
raise Error, WRONG % [2, arguments.size] if arguments.size != 2
answer = arguments[0] / arguments[1]
- return answer.to_i.to_s if answer.to_i == answer
- FLOAT_FORMAT % answer
end
def mod
arguments = fetch_arguments
raise Error, WRONG % [2, arguments.size] if arguments.size != 2
answer = arguments[0] % arguments[1]
- return answer.to_i.to_s if answer.to_i == answer
- FLOAT_FORMAT % answer
end
end
private
def fetch_table(text)
table = []
rows = text.split("\n")
rows = rows.map { |e| e.strip }.select { |e| e != "" }
- rows.each { |row| table << fetch_row(row) }
+ #rows.each { |row| table << fetch_row(row) }
+ rows.each { |row| table << row.split(/ {2,}|\t/).map(&:strip) }
table
end
- def fetch_row(string)
- row = []
- string = string.split(/ {2,}|\t/)
- string.map { |e| e.strip }.each { |cell| row << cell }
- row
- end
-
def formula_cell(cell)
- number = cell.match(/= *([0-9.]+)/)
- return number[1].to_i.to_s if number != nil and number[1].to_i == number[1]
- return "%.2f" % number[1] if number != nil
+ number = cell.match(/= *([0-9.]+)\Z/)
+ return number[1] if number != nil
is_reference = cell.match(/\A=([A-Z]+)([1-9][0-9]*)\Z/)
return self[is_reference[0][1 .. -1]] if is_reference != nil
Formula.new(self, cell[1..-1]).evaluate
end
end

Ивайло обнови решението на 11.01.2016 14:35 (преди над 8 години)

class Spreadsheet
class Error < StandardError
def initialize(message)
super
end
end
attr_accessor :table
def initialize(text = nil)
if text == nil
@table = []
else
@table = fetch_table(text)
end
end
def empty?
@table == []
end
def cell_at(string)
index = TableIndex.new string
row, column = index.integer_indexes[0], index.integer_indexes[1]
begin
@table[row][column]
rescue StandardError
raise Error, "Cell '#{string}' does not exist"
end
end
def [](string)
result = self.calculate(string).to_s
number = result.match(/\A([0-9.]+)\Z/)
return result if number == nil or cell_at(string).match(/\A=/) == nil
return number[1].to_i.to_s if number[1].to_i == Float(number[1])
"%.2f" % number[1]
end
def calculate(string)
cell = cell_at(string)
if cell[0] == '='
formula_cell cell
else
cell
end
end
def to_s
table = @table.map.with_index do |row, i|
row.map.with_index { |cell, j| self[TableIndex.get_string_index(i, j)]}
end
table = table.map { |row| row.join("\t") }
table.join("\n")
end
class TableIndex
attr_accessor :integer_indexes
def initialize(string)
@table_indexes = fetch_indexes(string)
@integer_indexes = get_integer_indexes
end
def get_integer_indexes
indexes = @table_indexes
column = integer_column_index indexes[0]
row = indexes[1].to_i
[row - 1, column - 1]
end
def integer_column_index(string)
offset = 'A'.ord - 1
string.chars.inject(0){ |x,c| x * 26 + c.ord - offset }
end
def fetch_indexes(string)
match = string.match(/\A([A-Z]+)([1-9][0-9]*)\Z/)
if match != nil
[match[1], match[2]]
else
raise Spreadsheet::Error, "Ivalid cell index '#{string}'"
end
end
def self.get_string_index(x, y)
x = x + 1
hash = Hash.new { |hash,key| hash[key] = hash[key - 1].next }
hash = hash.merge({0 => "A"})
hash[y] + x.to_s
end
end
class Formula
FORMULAS = ["ADD", "MULTIPLY", "SUBTRACT", "DIVIDE", "MOD"]
LESS = "Wrong number of arguments for 'FOO': expected at least %s, got $s"
WRONG = "Wrong number of arguments for 'FOO': expected %s, got %s"
FLOAT_FORMAT = "%.2f"
def initialize(table, string)
@table = table
@string = string
end
def evaluate
formula = @string.match(/\A([A-Z]+)\([0-9A-Z.]+(, [0-9A-Z.]+)*\)\Z/)
raise Error, "Invalid expression '#{@string}'" if formula == nil
unless FORMULAS.include? formula[1]
raise Error, "Unknown function '#{formula[1]}'"
else
self.send formula[1].downcase.to_sym
end
end
def fetch_arguments
formula = @string.match(/\([0-9A-Z.]+(, [0-9A-Z.]+)*\)\Z/)
arguments = formula[0][1..-2].split(',').map(&:strip)
arguments = arguments.map do |argument|
if argument.match(/[A-Z]/) != nil
Float(@table.calculate(argument))
else
Float(argument)
end
end
end
def add
arguments = fetch_arguments
raise Error, LESS % [2, arguments.size] if arguments.size < 2
answer = arguments.inject(:+)
end
def multiply
arguments = fetch_arguments
raise Error, LESS % [2, arguments.size] if arguments.size < 2
answer = arguments.inject(:*)
end
def subtract
arguments = fetch_arguments
raise Error, WRONG % [2, arguments.size] if arguments.size != 2
answer = arguments[0] - arguments[1]
end
def divide
arguments = fetch_arguments
raise Error, WRONG % [2, arguments.size] if arguments.size != 2
answer = arguments[0] / arguments[1]
end
def mod
arguments = fetch_arguments
raise Error, WRONG % [2, arguments.size] if arguments.size != 2
answer = arguments[0] % arguments[1]
end
end
private
def fetch_table(text)
table = []
rows = text.split("\n")
rows = rows.map { |e| e.strip }.select { |e| e != "" }
- #rows.each { |row| table << fetch_row(row) }
rows.each { |row| table << row.split(/ {2,}|\t/).map(&:strip) }
table
end
def formula_cell(cell)
number = cell.match(/= *([0-9.]+)\Z/)
return number[1] if number != nil
is_reference = cell.match(/\A=([A-Z]+)([1-9][0-9]*)\Z/)
return self[is_reference[0][1 .. -1]] if is_reference != nil
Formula.new(self, cell[1..-1]).evaluate
end
end