Решение на Осма задача от Димитър Узунов

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

Към профила на Димитър Узунов

Резултати

  • 2 точки от тестове
  • 0 бонус точки
  • 2 точки общо
  • 16 успешни тест(а)
  • 24 неуспешни тест(а)

Код

class Spreadsheet
def initialize(sheet = '')
@sheet = sheet.strip.split("\n").map { |row| row.strip.split(/\t| +/) }
end
def empty?
@sheet.empty?
end
def cell_at(cell_index)
row = row_index(cell_index)
column = column_index(cell_index)
raise Error, "Invalid cell index '#{cell_index}'" unless row and column
unless @sheet[row] and @sheet[row][column]
raise Error, "Cell '#{cell_index}' does not exist"
end
@sheet[row][column]
end
def [](cell_index)
Expression.create(cell_at(cell_index), self).evaluate.to_s
end
def to_s
@sheet.map do |row|
row.map { |cell| Expression.create(cell, self).evaluate.to_s }.join("\t")
end.join("\n")
end
private
def row_index(cell_index)
matched = cell_index.match /^[A-Z]+(\d+)$/
matched ? matched[1].to_i - 1 : nil
end
def column_index(cell_index)
if (matched = cell_index.match /^([A-Z]+)\d+$/)
numbers = matched[1].each_char.map { |char| char.ord - 'A'.ord + 1}
numbers.reverse.each_with_index.reduce(0) do | (sum, _), (number, index) |
sum + number * 26**index
end - 1
end
end
end
module Spreadsheet::Expression
def self.create(raw, sheet)
if (matched = raw.match /^= *(\d+(\.\d+)?)$/)
Number.new(matched[1])
elsif (matched = raw.match /^= *([A-Z]+\d+)$/)
Reference.new(matched[1], sheet)
else
raw[0] == '=' ? Formula.new(raw[1..-1], sheet) : String.new(raw)
end
end
end
class Spreadsheet::Expression::String
def initialize(string)
@string = string
end
def evaluate
@string
end
end
class Spreadsheet::Expression::Number
def initialize(number)
@number = number
end
def evaluate
@number.to_f
end
end
class Spreadsheet::Expression::Reference
def initialize(reference, sheet)
@reference = reference
@sheet = sheet
end
def evaluate
@sheet[@reference]
end
end
module Spreadsheet::Expression::Functions
def add(sum, current)
sum + current
end
def multiply(product, current)
product * current
end
def subtract(first, second)
sum - current
end
def divide(first, second)
first.to_f / second
end
def mod(first, second)
first % second
end
end
class Spreadsheet::Expression::Formula
include Spreadsheet::Expression::Functions
VALID_EXPRESSION = /^(ADD|MULTIPLY|SUBTRACT|DIVIDE|MOD)\((\w+(, *\w+)*)?\)$/
PARAMETERS_COUNT = {add: 3, multiply: 3, subtract: 2, divide: 2, mod: 2}
def initialize(formula, sheet)
@formula = formula
@sheet = sheet
end
def evaluate
function_type = get_function_type
matched = @formula.match VALID_EXPRESSION
raise Spreadsheet::Error, "Invalid expression '#{@formula}'" unless matched
arguments(function_type).map do |expression|
Spreadsheet::Expression.create("=#{expression}", @sheet).evaluate
end.reduce { |a, b| send(function_type.downcase.to_sym, a, b) }
end
private
def get_function_type
type = @formula.match /^ADD|MULTIPLY|SUBTRACT|DIVIDE|MOD/
unless type
wrong_name = @formula.match(/^\w*/)[0]
raise Spreadsheet::Error, "Unknown function '#{wrong_name}'"
end
type[0]
end
def arguments(function_type)
arguments_list = @formula.match(/\((.*)\)/)[1].split(/, */)
validate_arguments(arguments_list, function_type)
arguments_list
end
def validate_arguments(arguments_list, function_type)
arguments_count = arguments_list.length
parameters_count = PARAMETERS_COUNT[function_type.downcase.to_sym]
if parameters_count == 2 and arguments_count != 2 or
parameters_count > 2 and arguments_count < 2
raise_exceptions(arguments_count, parameters_count, function_type)
end
end
def raise_exceptions(arguments_count, parameters_count, function_type)
message = "Wrong number of arguments for '#{function_type}': expected "
if parameters_count == 2 and arguments_count != 2
message += "#{parameters_count}, got #{arguments_count}"
elsif parameters_count > 2 and arguments_count < 2
message += "at least #{parameters_count}, got #{arguments_count}"
end
raise Spreadsheet::Error, message
end
end
class Spreadsheet::Error < StandardError
end

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

.........F.....FFFFFFFFFFFFFFFFFFFFFF..F

Failures:

  1) Spreadsheet#to_s returns the evaluated spreadsheet as a table
     Failure/Error: expect(sheet.to_s).to eq \
     Spreadsheet::Error:
       Invalid expression 'ADD(B1, C1, 2.9)'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:129:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:27:in `block (2 levels) in to_s'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:27:in `map'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:27:in `block in to_s'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:26:in `map'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:26:in `to_s'
     # /tmp/d20160121-5693-1ocxzig/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#[] returns the calculated value of formulae cells
     Failure/Error: expect(sheet['C1']).to eq '4'
       
       expected: "4"
            got: "4.0"
       
       (compared using ==)
     # /tmp/d20160121-5693-1ocxzig/spec.rb:103: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#[] adds two numbers with ADD
     Failure/Error: expect(sheet['A1']).to eq('4')
       
       expected: "4"
            got: "4.0"
       
       (compared using ==)
     # /tmp/d20160121-5693-1ocxzig/spec.rb:109: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#[] adds five numbers with ADD
     Failure/Error: expect(sheet['A1']).to eq('15')
       
       expected: "15"
            got: "15.0"
       
       (compared using ==)
     # /tmp/d20160121-5693-1ocxzig/spec.rb:115: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 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 'ADD': expected at least 3, got 1> with backtrace:
         # /tmp/d20160121-5693-1ocxzig/solution.rb:172:in `raise_exceptions'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:160:in `validate_arguments'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:151:in `arguments'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:131:in `evaluate'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
         # /tmp/d20160121-5693-1ocxzig/spec.rb:119:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1ocxzig/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-1ocxzig/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)>'

  6) Spreadsheet#[] adds numbers from cell references and as immediate arguments with ADD
     Failure/Error: expect(sheet['B1']).to eq('55')
     TypeError:
       String can't be coerced into Float
     # /tmp/d20160121-5693-1ocxzig/solution.rb:93:in `+'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:93:in `add'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `block in evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `each'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `reduce'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
     # /tmp/d20160121-5693-1ocxzig/spec.rb:131: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 numbers only from cell references with ADD
     Failure/Error: expect(sheet['D1']).to eq('10')
       
       expected: "10"
            got: "325"
       
       (compared using ==)
     # /tmp/d20160121-5693-1ocxzig/spec.rb:137: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#[] multiplies numbers with MULTIPLY
     Failure/Error: expect(sheet1['A1']).to eq('120')
       
       expected: "120"
            got: "120.0"
       
       (compared using ==)
     # /tmp/d20160121-5693-1ocxzig/spec.rb:144: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#[] 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 'MULTIPLY': expected at least 3, got 1> with backtrace:
         # /tmp/d20160121-5693-1ocxzig/solution.rb:172:in `raise_exceptions'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:160:in `validate_arguments'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:151:in `arguments'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:131:in `evaluate'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
         # /tmp/d20160121-5693-1ocxzig/spec.rb:149:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1ocxzig/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-1ocxzig/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)>'

  10) Spreadsheet#[] subtracts two numbers with SUBTRACT
     Failure/Error: expect(sheet['A1']).to eq('2')
     NameError:
       undefined local variable or method `sum' for #<Spreadsheet::Expression::Formula:0x007fced5459a88>
     # /tmp/d20160121-5693-1ocxzig/solution.rb:101:in `subtract'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `block in evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `each'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `reduce'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
     # /tmp/d20160121-5693-1ocxzig/spec.rb:161: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#[] subtracts numbers via cell references
     Failure/Error: expect(sheet['D1']).to eq('4')
     NameError:
       undefined local variable or method `sum' for #<Spreadsheet::Expression::Formula:0x007fced5456950>
     # /tmp/d20160121-5693-1ocxzig/solution.rb:101:in `subtract'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `block in evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `each'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `reduce'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
     # /tmp/d20160121-5693-1ocxzig/spec.rb:167: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)>'

  12) Spreadsheet#[] raises an exception when SUBTRACT is called with a wrong number of arguments
     Failure/Error: expect { Spreadsheet.new('=SUBTRACT(1, 2, 3)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'SUBTRACT': expected 2, got 3/, got #<NameError: undefined local variable or method `sum' for #<Spreadsheet::Expression::Formula:0x007fced544e688>> with backtrace:
         # /tmp/d20160121-5693-1ocxzig/solution.rb:101:in `subtract'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `block in evaluate'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `each'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `reduce'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `evaluate'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
         # /tmp/d20160121-5693-1ocxzig/spec.rb:175:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1ocxzig/spec.rb:175: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-1ocxzig/spec.rb:175: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)>'

  13) Spreadsheet#[] divides two numbers with DIVIDE
     Failure/Error: expect(sheet['A1']).to eq('42')
       
       expected: "42"
            got: "42.0"
       
       (compared using ==)
     # /tmp/d20160121-5693-1ocxzig/spec.rb:183: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)>'

  14) Spreadsheet#[] divides numbers via cell references
     Failure/Error: expect(sheet1['C1']).to eq('42')
     TypeError:
       String can't be coerced into Float
     # /tmp/d20160121-5693-1ocxzig/solution.rb:105:in `/'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:105:in `divide'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `block in evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `each'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `reduce'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
     # /tmp/d20160121-5693-1ocxzig/spec.rb:190: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)>'

  15) Spreadsheet#[] raises an exception when DIVIDE is called with a wrong number of arguments
     Failure/Error: expect { Spreadsheet.new('=DIVIDE(1, 2, 3)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'DIVIDE': expected 2, got 3/ but nothing was raised
     # /tmp/d20160121-5693-1ocxzig/spec.rb:199: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)>'

  16) Spreadsheet#[] calculates the modulo of two numbers with MOD
     Failure/Error: expect(Spreadsheet.new('=MOD(42, 5)')['A1']).to eq('2')
       
       expected: "2"
            got: "2.0"
       
       (compared using ==)
     # /tmp/d20160121-5693-1ocxzig/spec.rb:205: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)>'

  17) Spreadsheet#[] calculates the modulo of two numbers with MOD via cell references
     Failure/Error: expect(sheet1['C1']).to eq('4')
       
       expected: "4"
            got: "84"
       
       (compared using ==)
     # /tmp/d20160121-5693-1ocxzig/spec.rb:214: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)>'

  18) Spreadsheet#[] raises an exception when MOD is called with a wrong number of arguments
     Failure/Error: expect { Spreadsheet.new('=MOD(1, 2, 3)')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'MOD': expected 2, got 3/ but nothing was raised
     # /tmp/d20160121-5693-1ocxzig/spec.rb:223: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)>'

  19) Spreadsheet#[] adds floating point numbers with ADD
     Failure/Error: expect(Spreadsheet.new('10  =ADD(A1, 1.1)')['B1']).to eq '11.10'
     Spreadsheet::Error:
       Invalid expression 'ADD(A1, 1.1)'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:129:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
     # /tmp/d20160121-5693-1ocxzig/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)>'

  20) Spreadsheet#[] subtracts floating point numbers with SUBTRACT
     Failure/Error: expect(Spreadsheet.new('10  =SUBTRACT(A1, 1.1)')['B1']).to eq '8.90'
     Spreadsheet::Error:
       Invalid expression 'SUBTRACT(A1, 1.1)'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:129:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
     # /tmp/d20160121-5693-1ocxzig/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)>'

  21) Spreadsheet#[] multiplies floating point numbers with MULTIPLY
     Failure/Error: expect(Spreadsheet.new('10  =MULTIPLY(A1, 1.1)')['B1']).to eq '11'
     Spreadsheet::Error:
       Invalid expression 'MULTIPLY(A1, 1.1)'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:129:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
     # /tmp/d20160121-5693-1ocxzig/spec.rb:239: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)>'

  22) 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-1ocxzig/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)>'

  23) Spreadsheet#[] evaluates deeply-nested cell references
     Failure/Error: expect(Spreadsheet.new('10  =ADD(5, A1)  3  =DIVIDE(B1, C1)  =MOD(D1, 4)')['E1']).to eq '1'
     TypeError:
       String can't be coerced into Float
     # /tmp/d20160121-5693-1ocxzig/solution.rb:93:in `+'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:93:in `add'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `block in evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `each'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `reduce'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:133:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:87:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:132:in `block in evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:131:in `map'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:131:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:87:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:132:in `block in evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:131:in `map'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:131:in `evaluate'
     # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
     # /tmp/d20160121-5693-1ocxzig/spec.rb:250: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)>'

  24) 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'/, got #<Spreadsheet::Error: Unknown function 'FOO'> with backtrace:
         # /tmp/d20160121-5693-1ocxzig/solution.rb:143:in `get_function_type'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:126:in `evaluate'
         # /tmp/d20160121-5693-1ocxzig/solution.rb:22:in `[]'
         # /tmp/d20160121-5693-1ocxzig/spec.rb:266:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1ocxzig/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)>'
     # /tmp/d20160121-5693-1ocxzig/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.03026 seconds
40 examples, 24 failures

Failed examples:

rspec /tmp/d20160121-5693-1ocxzig/spec.rb:43 # Spreadsheet#to_s returns the evaluated spreadsheet as a table
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:98 # Spreadsheet#[] returns the calculated value of formulae cells
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:106 # Spreadsheet#[] adds two numbers with ADD
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:112 # Spreadsheet#[] adds five numbers with ADD
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:118 # Spreadsheet#[] raises an exception for less than two arguments passed to ADD
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:128 # Spreadsheet#[] adds numbers from cell references and as immediate arguments with ADD
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:134 # Spreadsheet#[] adds numbers only from cell references with ADD
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:140 # Spreadsheet#[] multiplies numbers with MULTIPLY
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:148 # Spreadsheet#[] raises an exception for less than two arguments to MULTIPLY
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:158 # Spreadsheet#[] subtracts two numbers with SUBTRACT
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:164 # Spreadsheet#[] subtracts numbers via cell references
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:170 # Spreadsheet#[] raises an exception when SUBTRACT is called with a wrong number of arguments
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:180 # Spreadsheet#[] divides two numbers with DIVIDE
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:186 # Spreadsheet#[] divides numbers via cell references
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:194 # Spreadsheet#[] raises an exception when DIVIDE is called with a wrong number of arguments
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:204 # Spreadsheet#[] calculates the modulo of two numbers with MOD
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:210 # Spreadsheet#[] calculates the modulo of two numbers with MOD via cell references
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:218 # Spreadsheet#[] raises an exception when MOD is called with a wrong number of arguments
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:228 # Spreadsheet#[] adds floating point numbers with ADD
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:233 # Spreadsheet#[] subtracts floating point numbers with SUBTRACT
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:238 # Spreadsheet#[] multiplies floating point numbers with MULTIPLY
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:243 # Spreadsheet#[] divides floating point numbers with DIVIDE
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:249 # Spreadsheet#[] evaluates deeply-nested cell references
rspec /tmp/d20160121-5693-1ocxzig/spec.rb:265 # Spreadsheet#[] raises an exception for invalid expressions

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

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

+class Spreadsheet
+ def initialize(sheet = '')
+ @sheet = sheet.strip.split("\n").map { |row| row.strip.split(/\t| +/) }
+ end
+
+ def empty?
+ @sheet.empty?
+ end
+
+ def cell_at(cell_index)
+ row = row_index(cell_index)
+ column = column_index(cell_index)
+ raise Error, "Invalid cell index '#{cell_index}'" unless row and column
+
+ unless @sheet[row] and @sheet[row][column]
+ raise Error, "Cell '#{cell_index}' does not exist"
+ end
+ @sheet[row][column]
+ end
+
+ def [](cell_index)
+ Expression.create(cell_at(cell_index), self).evaluate.to_s
+ end
+
+ def to_s
+ @sheet.map do |row|
+ row.map { |cell| Expression.create(cell, self).evaluate.to_s }.join("\t")
+ end.join("\n")
+ end
+
+ private
+
+ def row_index(cell_index)
+ matched = cell_index.match /^[A-Z]+(\d+)$/
+ matched ? matched[1].to_i - 1 : nil
+ end
+
+ def column_index(cell_index)
+ if (matched = cell_index.match /^([A-Z]+)\d+$/)
+ numbers = matched[1].each_char.map { |char| char.ord - 'A'.ord + 1}
+ numbers.reverse.each_with_index.reduce(0) do | (sum, _), (number, index) |
+ sum + number * 26**index
+ end - 1
+ end
+ end
+end
+
+module Spreadsheet::Expression
+ def self.create(raw, sheet)
+ if (matched = raw.match /^= *(\d+(\.\d+)?)$/)
+ Number.new(matched[1])
+ elsif (matched = raw.match /^= *([A-Z]+\d+)$/)
+ Reference.new(matched[1], sheet)
+ else
+ raw[0] == '=' ? Formula.new(raw[1..-1], sheet) : String.new(raw)
+ end
+ end
+end
+
+class Spreadsheet::Expression::String
+ def initialize(string)
+ @string = string
+ end
+
+ def evaluate
+ @string
+ end
+end
+
+class Spreadsheet::Expression::Number
+ def initialize(number)
+ @number = number
+ end
+
+ def evaluate
+ @number.to_f
+ end
+end
+
+class Spreadsheet::Expression::Reference
+ def initialize(reference, sheet)
+ @reference = reference
+ @sheet = sheet
+ end
+
+ def evaluate
+ @sheet[@reference]
+ end
+end
+
+module Spreadsheet::Expression::Functions
+ def add(sum, current)
+ sum + current
+ end
+
+ def multiply(product, current)
+ product * current
+ end
+
+ def subtract(first, second)
+ sum - current
+ end
+
+ def divide(first, second)
+ first.to_f / second
+ end
+
+ def mod(first, second)
+ first % second
+ end
+end
+
+class Spreadsheet::Expression::Formula
+ include Spreadsheet::Expression::Functions
+
+ VALID_EXPRESSION = /^(ADD|MULTIPLY|SUBTRACT|DIVIDE|MOD)\((\w+(, *\w+)*)?\)$/
+
+ PARAMETERS_COUNT = {add: 3, multiply: 3, subtract: 2, divide: 2, mod: 2}
+
+ def initialize(formula, sheet)
+ @formula = formula
+ @sheet = sheet
+ end
+
+ def evaluate
+ function_type = get_function_type
+
+ matched = @formula.match VALID_EXPRESSION
+ raise Spreadsheet::Error, "Invalid expression '#{@formula}'" unless matched
+
+ arguments(function_type).map do |expression|
+ Spreadsheet::Expression.create("=#{expression}", @sheet).evaluate
+ end.reduce { |a, b| send(function_type.downcase.to_sym, a, b) }
+ end
+
+ private
+
+ def get_function_type
+ type = @formula.match /^ADD|MULTIPLY|SUBTRACT|DIVIDE|MOD/
+
+ unless type
+ wrong_name = @formula.match(/^\w*/)[0]
+ raise Spreadsheet::Error, "Unknown function '#{wrong_name}'"
+ end
+
+ type[0]
+ end
+
+ def arguments(function_type)
+ arguments_list = @formula.match(/\((.*)\)/)[1].split(/, */)
+ validate_arguments(arguments_list, function_type)
+ arguments_list
+ end
+
+ def validate_arguments(arguments_list, function_type)
+ arguments_count = arguments_list.length
+ parameters_count = PARAMETERS_COUNT[function_type.downcase.to_sym]
+ if parameters_count == 2 and arguments_count != 2 or
+ parameters_count > 2 and arguments_count < 2
+ raise_exceptions(arguments_count, parameters_count, function_type)
+ end
+ end
+
+ def raise_exceptions(arguments_count, parameters_count, function_type)
+ message = "Wrong number of arguments for '#{function_type}': expected "
+ if parameters_count == 2 and arguments_count != 2
+ message += "#{parameters_count}, got #{arguments_count}"
+ elsif parameters_count > 2 and arguments_count < 2
+ message += "at least #{parameters_count}, got #{arguments_count}"
+ end
+
+ raise Spreadsheet::Error, message
+ end
+end
+
+class Spreadsheet::Error < StandardError
+end