Мирослав обнови решението на 11.01.2016 14:51 (преди около 9 години)
+class Spreadsheet
+
+ attr_accessor :table
+
+ def initialize(data = "")
+ @table = Table.new
+ data.strip.split(/\n/).each { |line|
+ @table.add_row(line.strip.split(/\s\s+|\t/))
+ }
+ end
+
+ def empty?()
+ not @table.has_rows?
+ end
+
+ def cell_at(cell_index)
+ get_cell(cell_index)
+ end
+
+ def [](cell_index)
+ get_cell(cell_index, true)
+ end
+
+ def to_s
+ @table.to_s
+ end
+
+ class Error < Exception
+ end
+
+ private
+
+ def get_cell(cell_index, evaluate = false)
+ if match = cell_index.match(Expression::ONLY_CELL_INDEX_PATTERN)
+ first, second = match.captures
+ @table.get(first, second.to_i, evaluate)
+ else
+ raise Spreadsheet::Error, "Invalid cell index '#{cell_index}'"
+ end
+ end
+end
+
+class CellIndexFunctions
+
+ ALPHABET_LENGTH = 26
+ FIRST_ALPHABET_CHARACTER = 65
+
+ def initialize()
+ end
+
+ def get_letter_at(index)
+ (index + FIRST_ALPHABET_CHARACTER - 1).chr
+ end
+
+ def convert_to_symbols(number)
+ if number <= ALPHABET_LENGTH
+ return get_letter_at(number)
+ end
+ convert_to_symbols(Rational(number, ALPHABET_LENGTH).floor) +
+ get_letter_at(number % ALPHABET_LENGTH)
+ end
+end
+
+class Table
+
+ attr_accessor :cells
+
+ def initialize()
+ @cells = {}
+ @max_index = 0
+ @max_rows = 0
+ end
+
+ def initialize_column(index)
+ if @cells[CellIndexFunctions.new.convert_to_symbols(index + 1)].nil?
+ @cells[CellIndexFunctions.new.convert_to_symbols(index + 1)] = []
+ end
+ end
+
+ def add_row(row)
+ row.each.with_index { |element, index|
+ initialize_column(index)
+ if @max_index < index then @max_index = index end
+ @cells[CellIndexFunctions.new.convert_to_symbols(index + 1)].push(element)
+ }
+ @max_rows += 1
+ end
+
+ def has_rows?()
+ !@cells.empty?
+ end
+
+ def get(col, row, evaluate = false)
+ if @cells[col].nil? || @cells[col][row - 1].nil? || row - 1 < 0
+ raise Spreadsheet::Error, "Cell '#{col}#{row}' does not exist"
+ elsif evaluate
+ evaluate_cell(@cells[col][row - 1])
+ else
+ @cells[col][row - 1]
+ end
+ end
+
+ def evaluate_cell(cell)
+ Expression.new(self).evaluate(cell)
+ end
+
+ def to_s()
+ (0...@max_rows).reduce("") { |answer, row|
+ answer += (1..(@max_index + 1)).reduce(answer) { |line, index|
+ line += evaluate_cell(@cells[
+ CellIndexFunctions.new.convert_to_symbols(index)][row]).to_s + "\t"
+ }.strip + "\n"
+ }.strip
+ end
+end
+
+class Expression
+
+ EXPRESSION_PATTERN = /=(.*)/
+ RESULT_PATTERN = /(\d+)|(\d+\.\d+)/
+ ONLY_RESULT_PATTERN = /^#{RESULT_PATTERN}$/
+ CELL_INDEX_PATTERN = /([A-Z]+)([0-9]+)/
+ ONLY_CELL_INDEX_PATTERN = /^#{CELL_INDEX_PATTERN}$/
+ ARGUMENT_PATTERN = /#{RESULT_PATTERN}|#{CELL_INDEX_PATTERN}/
+ ARGUMENTS_PATTERN = /(?:#{ARGUMENT_PATTERN},\s?)*(?:#{ARGUMENT_PATTERN})/
+ FORMULA_PATTERN = /^(\w+)\((#{ARGUMENTS_PATTERN})?\)$/
+
+ def initialize(table)
+ @table = table
+ end
+
+ def evaluate(expression)
+ if match = expression.match(EXPRESSION_PATTERN)
+ assert_and_match_correct_expression(match.captures[0])
+ else
+ expression
+ end
+ end
+
+ def assert_and_match_correct_expression(expression_value)
+ match = expression_value.match(ONLY_RESULT_PATTERN) ||
+ expression_value.match(ONLY_CELL_INDEX_PATTERN) ||
+ expression_value.match(FORMULA_PATTERN)
+
+ if match.nil?
+ raise Spreadsheet::Error, "Invalid expression #{expression_value}"
+ else
+ match_pattern(expression_value)
+ end
+ end
+
+ def match_pattern(expression_value)
+ if match = expression_value.match(ONLY_RESULT_PATTERN)
+ match.captures[0] || match.captures[1]
+ elsif match = expression_value.match(ONLY_CELL_INDEX_PATTERN)
+ @table.get(match.captures[0], match.captures[1].to_i, true)
+ else match = expression_value.match(FORMULA_PATTERN)
+ assign_value(expression_value, match.captures[0], match.captures[1])
+ end
+ end
+
+ def assign_value(expression_value, formula_name, arguments)
+ if arguments.nil? then arguments = "" end
+ begin
+ Functions.new.send(formula_name.downcase, arguments.split(/,\s?/).map {
+ |argument| assert_and_match_correct_expression(argument).to_r
+ })
+ rescue NoMethodError => e
+ raise Spreadsheet::Error, "Unknown function '#{formula_name}'"
+ end
+ end
+end
+
+class Functions
+
+ UNKNOWN_NUMBER = "UNKNOWN_NUMBER"
+ CALLER = "CALLEE"
+ ACTUAL = "ACTUAL_AMOUNT"
+ EXPECTED_ARGUMENTS_ERROR_MESSAGE =
+ "Wrong number of arguments for #{CALLER}: expected 2, " +
+ "got #{ACTUAL}"
+ EXPECTED_AT_LEAST_ARGUMENTS_ERROR_MESSAGE =
+ "Wrong number of arguments for #{CALLER}: " +
+ "expected at least 2, got #{ACTUAL}"
+
+ def check_and_return(result)
+ result.denominator == 1 ? result.to_i.to_s : result.to_f.to_s
+ end
+
+ def create_error_message(message, actual, caller)
+ message.gsub(ACTUAL, actual).gsub(CALLER, caller)
+ end
+
+ def add(arguments)
+ if (2..(1.0 / 0)).include?(arguments.size)
+ check_and_return(arguments.reduce(:+))
+ else
+ raise Spreadsheet::Error,
+ create_error_message(EXPECTED_AT_LEAST_ARGUMENTS_ERROR_MESSAGE,
+ arguments.size.to_s, __callee__.to_s.upcase)
+ end
+ end
+
+ def multiply(arguments)
+ if (2..(1.0 / 0)).include?(arguments.size)
+ check_and_return(arguments.reduce(:*))
+ else
+ raise Spreadsheet::Error,
+ create_error_message(EXPECTED_AT_LEAST_ARGUMENTS_ERROR_MESSAGE,
+ arguments.size.to_s, __callee__.to_s.upcase)
+ end
+ end
+
+ def subtract(arguments)
+ if arguments.size == 2
+ check_and_return(arguments[0] - arguments[1])
+ else
+ raise Spreadsheet::Error,
+ create_error_message(EXPECTED_ARGUMENTS_ERROR_MESSAGE,
+ arguments.size.to_s, __callee__.to_s.upcase)
+ end
+ end
+
+ def divide(arguments)
+ if arguments.size == 2
+ check_and_return(arguments[0] / arguments[1])
+ else
+ raise Spreadsheet::Error,
+ create_error_message(EXPECTED_ARGUMENTS_ERROR_MESSAGE,
+ arguments.size.to_s, __callee__.to_s.upcase)
+ end
+ end
+
+ def mod(arguments)
+ if arguments.size == 2
+ check_and_return(arguments[0] % arguments[1])
+ else
+ raise Spreadsheet::Error,
+ create_error_message(EXPECTED_ARGUMENTS_ERROR_MESSAGE,
+ arguments.size.to_s, __callee__.to_s.upcase)
+ end
+ end
+end
+
+sheet = Spreadsheet.new <<-Table
+ 1 asd =ADD(A2, B2)
+ =A1 5
+ =ADD(1,3) =MOD(13, 7) =ADD(A3, B3)
+Table
+p sheet.to_s