Решение на Осма задача от Добромир Иванов

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

Към профила на Добромир Иванов

Резултати

  • 4 точки от тестове
  • 0 бонус точки
  • 4 точки общо
  • 29 успешни тест(а)
  • 11 неуспешни тест(а)

Код

class Spreadsheet
class Error < ArgumentError
end
end
class Spreadsheet
NUMERIC_SYSTEM = {
A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8, I:9, J: 10, K: 11, L: 12,
M: 13, N: 14, O: 15, P: 16, Q: 17, R: 18, S: 19, T: 20, U: 21, V: 22, W: 23,
X: 26, Y: 25, Z: 26
}
def initialize(sheet = "")
sheet = sheet.strip.split("\n").map(&:strip)
@sheet = sheet.map do |row|
row = row.split(/(\t|\s{2})/).map { |cell| cell.strip }
row.reject { |cell| cell.empty? }
end
end
def empty?
not @sheet.any?
end
def cell_at(cell_index)
row, column = to_decimal(cell_index)
if row >= @sheet.length || column >= @sheet[row].length
raise Error, "Cell '#{cell_index}' does not exist"
end
@sheet[row][column]
end
def [](cell_index)
cell = cell_at(cell_index)
if cell[0] == "="
Expression.evaluate_cell(cell, self)
else
cell
end
end
def to_s
result = @sheet.map do |row|
row.map do |column|
Expression.evaluate_cell(column, self)
end
end
result.map {|row| row.join("\t")}.join("\n")
end
private
def to_decimal(cell_index)
column, row = parse(cell_index)
column = column.split("").reverse
column = column.each_with_index.inject(0) do |sum, (symbol, index) |
sum + NUMERIC_SYSTEM[symbol.to_sym] * (26 ** index)
end
return row.to_i - 1, column - 1
end
def parse(cell_index)
matches = cell_index.match(/([A-Z]+)([1-9]+)/)
raise Error, "Invalid cell index '#{cell_index}'" if matches.nil?
matches.captures
end
end
class Expression
def initialize(operation)
@operation = operation
end
def self.valid_signature?(signature)
signature.match(/\(\s*,\s*/).nil? && signature.match(/\s*,\s*\)/).ni?
end
def self.arguments(signature)
start = signature.index("(") + 1
edge = signature.index(")")
signature[start...edge].split(",").map(&:strip)
end
def self.evaluate(signature, sheet)
expression = ExpressionFactory.get(signature)
arguments = arguments(signature).map do |argument|
if ! argument.match(/\A[A-Z]+\d+\z/).nil?
self.evaluate_cell(argument, sheet)
else
argument
end
end
expression.evaluate(arguments)
end
def self.evaluate_cell(cell, sheet)
if ! cell.match(/\A=[A-Z]+\d+\z/).nil? ||
! cell.match(/\A[A-Z]+\d+\z/).nil?
return evaluate_cell(sheet[cell.gsub(/\A=/, "")], sheet)
elsif ! cell.match(/\A=[A-Z]+\(/).nil?
return Expression.evaluate(cell, sheet)
end
cell.gsub(/\A=/, "")
end
def evaluate(arguments)
validate(arguments)
arguments = arguments.map(&:to_f)
result = arguments.reduce(&@operation)
if result.denominator == 1 then result.round.to_s else result.to_s end
end
end
class Add < Expression
def initialize(operation)
super(operation)
end
private
def validate(arguments)
if arguments.length < 2
raise Error, "Wrong number of arguments for " +
"'ADD': expected at least 2, got #{arguments.length}"
end
end
end
class Multiply < Expression
def initialize(operation)
super(operation)
end
private
def validate(arguments)
if arguments.length < 2
raise Error, "Wrong number of arguments for " +
"'MULTIPLY': expected at least 2, got #{arguments.length}"
end
end
end
class Subtract < Expression
def initialize(operation)
super(operation)
end
private
def validate(arguments)
if arguments.length != 2
raise Error, "Wrong number of arguments for 'SUBTRACT': " +
"expected 2, got #{arguments.length}"
end
end
end
class Divide < Expression
def initialize(operation)
super(operation)
end
private
def validate(arguments)
if arguments.length != 2
raise Error, "Wrong number of arguments for 'DIVIDE': " +
"expected 2, got #{arguments.length}"
end
end
end
class Mod < Expression
def initialize(operation)
super(operation)
end
private
def validate(arguments)
if arguments.length != 2
raise Error, "Wrong number of arguments for 'MOD': " +
"expected 2, got #{arguments.length}"
end
end
end
class ExpressionFactory
EXPRESSIONS = {
"ADD" => Add.new(:+), "MULTIPLY" => Multiply.new(:*),
"SUBTRACT" => Subtract.new(:-), "DIVIDE" => Divide.new(:/),
"MOD" => Mod.new(:%)
}
def self.get(expression)
name = expression[1...expression.index("(")]
if not EXPRESSIONS.keys.include?(name)
raise Eror, "Unknown function '#{name}'"
end
EXPRESSIONS[name]
end
end

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

.........F........F...F..F..F..FFF.F.F.F

Failures:

  1) Spreadsheet#to_s returns the evaluated spreadsheet as a table
     Failure/Error: expect(sheet.to_s).to eq \
       
       expected: "foo\t10\t2.1\t15\nbar\t11\t2.2\t5\nbaz\t12\t2.3\t27.60"
            got: "foo\t10\t2.1\t15\nbar\t11\t2.2\t5\nbaz\t12\t2.3\t27.599999999999998"
       
       (compared using ==)
       
       Diff:
       @@ -1,4 +1,4 @@
        foo	10	2.1	15
        bar	11	2.2	5
       -baz	12	2.3	27.60
       +baz	12	2.3	27.599999999999998
     # /tmp/d20160121-5693-b9nzh6/spec.rb:50: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 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 #<NameError: uninitialized constant Add::Error> with backtrace:
         # /tmp/d20160121-5693-b9nzh6/solution.rb:125:in `validate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:109:in `evaluate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:95:in `evaluate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:103:in `evaluate_cell'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:36:in `[]'
         # /tmp/d20160121-5693-b9nzh6/spec.rb:119:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-b9nzh6/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-b9nzh6/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)>'

  3) 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 #<NameError: uninitialized constant Multiply::Error> with backtrace:
         # /tmp/d20160121-5693-b9nzh6/solution.rb:140:in `validate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:109:in `evaluate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:95:in `evaluate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:103:in `evaluate_cell'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:36:in `[]'
         # /tmp/d20160121-5693-b9nzh6/spec.rb:149:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-b9nzh6/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-b9nzh6/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)>'

  4) 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 #<NameError: uninitialized constant Subtract::Error> with backtrace:
         # /tmp/d20160121-5693-b9nzh6/solution.rb:155:in `validate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:109:in `evaluate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:95:in `evaluate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:103:in `evaluate_cell'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:36:in `[]'
         # /tmp/d20160121-5693-b9nzh6/spec.rb:171:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-b9nzh6/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-b9nzh6/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)>'

  5) 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 #<NameError: uninitialized constant Divide::Error> with backtrace:
         # /tmp/d20160121-5693-b9nzh6/solution.rb:170:in `validate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:109:in `evaluate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:95:in `evaluate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:103:in `evaluate_cell'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:36:in `[]'
         # /tmp/d20160121-5693-b9nzh6/spec.rb:195:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-b9nzh6/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-b9nzh6/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)>'

  6) 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 #<NameError: uninitialized constant Mod::Error> with backtrace:
         # /tmp/d20160121-5693-b9nzh6/solution.rb:185:in `validate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:109:in `evaluate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:95:in `evaluate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:103:in `evaluate_cell'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:36:in `[]'
         # /tmp/d20160121-5693-b9nzh6/spec.rb:219:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-b9nzh6/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-b9nzh6/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)>'

  7) Spreadsheet#[] adds floating point numbers with ADD
     Failure/Error: expect(Spreadsheet.new('10  =ADD(A1, 1.1)')['B1']).to eq '11.10'
       
       expected: "11.10"
            got: "11.1"
       
       (compared using ==)
     # /tmp/d20160121-5693-b9nzh6/spec.rb:229: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)>'

  8) Spreadsheet#[] subtracts floating point numbers with SUBTRACT
     Failure/Error: expect(Spreadsheet.new('10  =SUBTRACT(A1, 1.1)')['B1']).to eq '8.90'
       
       expected: "8.90"
            got: "8.9"
       
       (compared using ==)
     # /tmp/d20160121-5693-b9nzh6/spec.rb:234: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)>'

  9) Spreadsheet#[] divides floating point numbers with DIVIDE
     Failure/Error: expect(Spreadsheet.new('10  =DIVIDE(A1, 4)')['B1']).to eq '2.50'
       
       expected: "2.50"
            got: "2.5"
       
       (compared using ==)
     # /tmp/d20160121-5693-b9nzh6/spec.rb:244: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)>'

  10) Spreadsheet#[] raises an exception for unknown functions
     Failure/Error: expect { Spreadsheet.new('=FOO(42)  100')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Unknown function 'FOO'/, got #<NameError: uninitialized constant ExpressionFactory::Eror> with backtrace:
         # /tmp/d20160121-5693-b9nzh6/solution.rb:203:in `get'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:87:in `evaluate'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:103:in `evaluate_cell'
         # /tmp/d20160121-5693-b9nzh6/solution.rb:36:in `[]'
         # /tmp/d20160121-5693-b9nzh6/spec.rb:254:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-b9nzh6/spec.rb:254: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-b9nzh6/spec.rb:254: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)>'

  11) Spreadsheet#[] raises an exception for invalid expressions
     Failure/Error: expect { Spreadsheet.new('=FOO  100')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Invalid expression 'FOO'/ but nothing was raised
     # /tmp/d20160121-5693-b9nzh6/spec.rb:266: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.04103 seconds
40 examples, 11 failures

Failed examples:

rspec /tmp/d20160121-5693-b9nzh6/spec.rb:43 # Spreadsheet#to_s returns the evaluated spreadsheet as a table
rspec /tmp/d20160121-5693-b9nzh6/spec.rb:118 # Spreadsheet#[] raises an exception for less than two arguments passed to ADD
rspec /tmp/d20160121-5693-b9nzh6/spec.rb:148 # Spreadsheet#[] raises an exception for less than two arguments to MULTIPLY
rspec /tmp/d20160121-5693-b9nzh6/spec.rb:170 # Spreadsheet#[] raises an exception when SUBTRACT is called with a wrong number of arguments
rspec /tmp/d20160121-5693-b9nzh6/spec.rb:194 # Spreadsheet#[] raises an exception when DIVIDE is called with a wrong number of arguments
rspec /tmp/d20160121-5693-b9nzh6/spec.rb:218 # Spreadsheet#[] raises an exception when MOD is called with a wrong number of arguments
rspec /tmp/d20160121-5693-b9nzh6/spec.rb:228 # Spreadsheet#[] adds floating point numbers with ADD
rspec /tmp/d20160121-5693-b9nzh6/spec.rb:233 # Spreadsheet#[] subtracts floating point numbers with SUBTRACT
rspec /tmp/d20160121-5693-b9nzh6/spec.rb:243 # Spreadsheet#[] divides floating point numbers with DIVIDE
rspec /tmp/d20160121-5693-b9nzh6/spec.rb:253 # Spreadsheet#[] raises an exception for unknown functions
rspec /tmp/d20160121-5693-b9nzh6/spec.rb:265 # Spreadsheet#[] raises an exception for invalid expressions

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

Добромир обнови решението на 11.01.2016 04:28 (преди над 8 години)

+class Spreadsheet
+ class Error < ArgumentError
+ end
+end
+
+class Spreadsheet
+ NUMERIC_SYSTEM = {
+ A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8, I:9, J: 10, K: 11, L: 12,
+ M: 13, N: 14, O: 15, P: 16, Q: 17, R: 18, S: 19, T: 20, U: 21, V: 22, W: 23,
+ X: 26, Y: 25, Z: 26
+ }
+
+ def initialize(sheet = "")
+ sheet = sheet.strip.split("\n").map(&:strip)
+ @sheet = sheet.map do |row|
+ row = row.split(/(\t|\s{2})/).map { |cell| cell.strip }
+ row.reject { |cell| cell.empty? }
+ end
+ end
+
+ def empty?
+ not @sheet.any?
+ end
+
+ def cell_at(cell_index)
+ row, column = to_decimal(cell_index)
+ if row >= @sheet.length || column >= @sheet[row].length
+ raise Error, "Cell '#{cell_index}' does not exist"
+ end
+ @sheet[row][column]
+ end
+
+ def [](cell_index)
+ cell = cell_at(cell_index)
+ if cell[0] == "="
+ evaluate_cell(cell)
+ else
+ cell
+ end
+ end
+
+ private
+
+ def to_decimal(cell_index)
+ column, row = parse(cell_index)
+ column = column.split("").reverse
+ column = column.each_with_index.inject(0) do |sum, (symbol, index) |
+ sum + NUMERIC_SYSTEM[symbol.to_sym] * (26 ** index)
+ end
+
+ return row.to_i - 1, column - 1
+ end
+
+ def parse(cell_index)
+ matches = cell_index.match(/([A-Z]+)([1-9]+)/)
+ raise Error, "Invalid cell index '#{cell_index}'" if matches.nil?
+ matches.captures
+ end
+
+ def evaluate_cell(cell)
+ if cell.match(/\A=\d+\z/)
+ return cell[1..-1]
+ elsif cell.match(/\A=[A-Z]+\d+\z/)
+ return evaluate_cell(cell[1..-1])
+ end
+
+ Expression.evaluate(cell, self)
+ end
+
+end
+
+class Expression
+ def initialize(operation)
+ @operation = operation
+ end
+
+ def self.valid_signature?(signature)
+ signature.match(/\(\s*,\s*/).nil? && signature.match(/\s*,\s*\)/).ni?
+ end
+
+ def self.arguments(signature)
+ start = signature.index("(") + 1
+ edge = signature.index(")")
+
+ signature[start...edge].split(",").map(&:strip)
+ end
+
+ def self.evaluate(signature, sheet)
+ expression = ExpressionFactory.get(signature)
+ arguments = arguments(signature).map do |argument|
+ if argument.match(/\A[A-Z]+\d+\z/)
+ sheet.evaluate_cell(argument)
+ else
+ argument
+ end
+ end
+
+ expression.evaluate(arguments)
+ end
+
+ def evaluate(arguments)
+ validate(arguments)
+ arguments = arguments.map(&:to_f)
+ result = arguments.reduce(&@operation)
+ if result.denominator == 1 then result.round.to_s else result.to_s end
+ end
+end
+
+class Add < Expression
+ def initialize(operation)
+ super(operation)
+ end
+
+ private
+
+ def validate(arguments)
+ if arguments.length < 2
+ raise Error, "Wrong number of arguments for " +
+ "'ADD': expected at least 2, got #{arguments.length}"
+ end
+ end
+end
+
+class Multiply < Expression
+ def initialize(operation)
+ super(operation)
+ end
+
+ private
+
+ def validate(arguments)
+ if arguments.length < 2
+ raise Error, "Wrong number of arguments for " +
+ "'MULTIPLY': expected at least 2, got #{arguments.length}"
+ end
+ end
+end
+
+class Subtract < Expression
+ def initialize(operation)
+ super(operation)
+ end
+
+ private
+
+ def validate(arguments)
+ if arguments.length != 2
+ raise Error, "Wrong number of arguments for 'SUBTRACT': " +
+ "expected 2, got #{arguments.length}"
+ end
+ end
+end
+
+class Divide < Expression
+ def initialize(operation)
+ super(operation)
+ end
+
+ private
+
+ def validate(arguments)
+ if arguments.length != 2
+ raise Error, "Wrong number of arguments for 'DIVIDE': " +
+ "expected 2, got #{arguments.length}"
+ end
+ end
+end
+
+class Mod < Expression
+ def initialize(operation)
+ super(operation)
+ end
+
+ private
+
+ def validate(arguments)
+ if arguments.length != 2
+ raise Error, "Wrong number of arguments for 'MOD': " +
+ "expected 2, got #{arguments.length}"
+ end
+ end
+end
+
+class ExpressionFactory
+
+ EXPRESSIONS = {
+ "ADD" => Add.new(:+), "MULTIPLY" => Multiply.new(:*),
+ "SUBTRACT" => Subtract.new(:-), "DIVIDE" => Divide.new(:/),
+ "MOD" => Mod.new(:%)
+ }
+
+ def self.get(expression)
+ name = expression[1...expression.index("(")]
+
+ if not EXPRESSIONS.keys.include?(name)
+ raise Eror, "Unknown function '#{name}'"
+ end
+
+ EXPRESSIONS[name]
+ end
+end

Добромир обнови решението на 11.01.2016 06:18 (преди над 8 години)

class Spreadsheet
class Error < ArgumentError
end
end
class Spreadsheet
NUMERIC_SYSTEM = {
A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8, I:9, J: 10, K: 11, L: 12,
M: 13, N: 14, O: 15, P: 16, Q: 17, R: 18, S: 19, T: 20, U: 21, V: 22, W: 23,
X: 26, Y: 25, Z: 26
}
def initialize(sheet = "")
sheet = sheet.strip.split("\n").map(&:strip)
@sheet = sheet.map do |row|
row = row.split(/(\t|\s{2})/).map { |cell| cell.strip }
row.reject { |cell| cell.empty? }
end
end
def empty?
not @sheet.any?
end
def cell_at(cell_index)
row, column = to_decimal(cell_index)
if row >= @sheet.length || column >= @sheet[row].length
raise Error, "Cell '#{cell_index}' does not exist"
end
@sheet[row][column]
end
def [](cell_index)
cell = cell_at(cell_index)
if cell[0] == "="
- evaluate_cell(cell)
+ Expression.evaluate_cell(cell, self)
else
cell
end
end
+ def to_s
+ result = @sheet.map do |row|
+ row.map do |column|
+ Expression.evaluate_cell(column, self)
+ end
+ end
+ result.map {|row| row.join("\t")}.join("\n")
+ end
+
private
def to_decimal(cell_index)
column, row = parse(cell_index)
column = column.split("").reverse
column = column.each_with_index.inject(0) do |sum, (symbol, index) |
sum + NUMERIC_SYSTEM[symbol.to_sym] * (26 ** index)
end
return row.to_i - 1, column - 1
end
def parse(cell_index)
matches = cell_index.match(/([A-Z]+)([1-9]+)/)
raise Error, "Invalid cell index '#{cell_index}'" if matches.nil?
matches.captures
end
-
- def evaluate_cell(cell)
- if cell.match(/\A=\d+\z/)
- return cell[1..-1]
- elsif cell.match(/\A=[A-Z]+\d+\z/)
- return evaluate_cell(cell[1..-1])
- end
-
- Expression.evaluate(cell, self)
- end
-
end
class Expression
def initialize(operation)
@operation = operation
end
def self.valid_signature?(signature)
signature.match(/\(\s*,\s*/).nil? && signature.match(/\s*,\s*\)/).ni?
end
def self.arguments(signature)
start = signature.index("(") + 1
edge = signature.index(")")
signature[start...edge].split(",").map(&:strip)
end
def self.evaluate(signature, sheet)
expression = ExpressionFactory.get(signature)
arguments = arguments(signature).map do |argument|
- if argument.match(/\A[A-Z]+\d+\z/)
- sheet.evaluate_cell(argument)
+ if ! argument.match(/\A[A-Z]+\d+\z/).nil?
+ self.evaluate_cell(argument, sheet)
else
argument
end
end
-
expression.evaluate(arguments)
+ end
+
+ def self.evaluate_cell(cell, sheet)
+ if ! cell.match(/\A=[A-Z]+\d+\z/).nil? ||
+ ! cell.match(/\A[A-Z]+\d+\z/).nil?
+ return evaluate_cell(sheet[cell.gsub(/\A=/, "")], sheet)
+ elsif ! cell.match(/\A=[A-Z]+\(/).nil?
+ return Expression.evaluate(cell, sheet)
+ end
+ cell.gsub(/\A=/, "")
end
def evaluate(arguments)
validate(arguments)
arguments = arguments.map(&:to_f)
result = arguments.reduce(&@operation)
if result.denominator == 1 then result.round.to_s else result.to_s end
end
end
class Add < Expression
def initialize(operation)
super(operation)
end
private
def validate(arguments)
if arguments.length < 2
raise Error, "Wrong number of arguments for " +
"'ADD': expected at least 2, got #{arguments.length}"
end
end
end
class Multiply < Expression
def initialize(operation)
super(operation)
end
private
def validate(arguments)
if arguments.length < 2
raise Error, "Wrong number of arguments for " +
"'MULTIPLY': expected at least 2, got #{arguments.length}"
end
end
end
class Subtract < Expression
def initialize(operation)
super(operation)
end
private
def validate(arguments)
if arguments.length != 2
raise Error, "Wrong number of arguments for 'SUBTRACT': " +
"expected 2, got #{arguments.length}"
end
end
end
class Divide < Expression
def initialize(operation)
super(operation)
end
private
def validate(arguments)
if arguments.length != 2
raise Error, "Wrong number of arguments for 'DIVIDE': " +
"expected 2, got #{arguments.length}"
end
end
end
class Mod < Expression
def initialize(operation)
super(operation)
end
private
def validate(arguments)
if arguments.length != 2
raise Error, "Wrong number of arguments for 'MOD': " +
"expected 2, got #{arguments.length}"
end
end
end
class ExpressionFactory
EXPRESSIONS = {
"ADD" => Add.new(:+), "MULTIPLY" => Multiply.new(:*),
"SUBTRACT" => Subtract.new(:-), "DIVIDE" => Divide.new(:/),
"MOD" => Mod.new(:%)
}
def self.get(expression)
name = expression[1...expression.index("(")]
if not EXPRESSIONS.keys.include?(name)
raise Eror, "Unknown function '#{name}'"
end
EXPRESSIONS[name]
end
end