Георги обнови решението на 08.01.2016 16:50 (преди около 9 години)
▸ Покажи разликите+class Spreadsheet
+private
+ def parse(text)
+ lines = text.strip.split(/\n/)
+ lines.map { |line| line.strip.split(/\t| {2,}/) }
+ end
+
+ def get_cell_index(cell_index)
+ return nil unless cell_index = Expression.index?(cell_index)
+
+ column = cell_index[1].each_byte.reverse_each.map { |byte| byte - 64 }
+ column.map!.with_index { |byte, index| byte * (26 ** index) }
+
+ row = cell_index[2].to_i - 1
+ column = column.reduce(:+) - 1
+
+ [row, column]
+ end
+
+ def validate_cell_index(cell_index)
+ row, column = get_cell_index cell_index
+ if row.nil?
+ raise Error, "Invalid cell index '#{cell_index}'"
+ elsif @table[row].nil? || @table[row][column].nil?
+ raise Error, "Cell '#{cell_index}' does not exist"
+ end
+ end
+
+public
+ def initialize(table = "")
+ @table = parse table
+ end
+
+ def empty?
+ @table.empty?
+ end
+
+ def cell_at(cell_index)
+ validate_cell_index cell_index
+
+ row, column = get_cell_index cell_index
+
+ @table[row][column]
+ end
+
+ def [](cell_index)
+ cell = cell_at cell_index
+
+ Expression.evaluate(cell, self).to_s
+ end
+
+ def to_s
+ @table.map do |row|
+ row.map{ |cell| Expression.evaluate(cell, self).to_s }.join("\t")
+ end.join("\n")
+ end
+
+ class Error < RuntimeError
+
+ end
+
+ module Expression
+ def self.match?(string)
+ not string.match(/^=/).nil?
+ end
+
+ def self.number?(string)
+ string.match(/^\d+(\.\d+)?$/)
+ end
+
+ def self.index?(cell_index)
+ cell_index.match(/^([A-Z]+)(\d+)$/)
+ end
+
+ def self.normalize(result)
+ result = ("%.2f" % result).to_f
+
+ result = result.floor if result == result.floor
+ result.to_s
+ end
+
+ def self.evaluate(string, sheet)
+ return string unless match? string
+
+ expression = string.match(/^=([\w.]+)/)[1]
+
+ return normalize(expression) if number? expression
+
+ return sheet[expression] if index? expression
+
+ normalize evaluate_formula(string, sheet)
+ end
+
+ def self.evaluate_formula(formula, sheet)
+ Formula.verify formula
+
+ formula, *args = formula.scan(/[\w.=]+/)
+ formula.sub!(/^=/, "").downcase!
+
+ args.map! { |argument| Expression.evaluate(argument, sheet).to_f }
+
+ Formula.send(formula, *args)
+ end
+ end
+
+ module Formula
+ def self.add(first, second, *more)
+ first + second + more.reduce(0, :+)
+ end
+
+ def self.multiply(first, second, *more)
+ first * second * more.reduce(1, :*)
+ end
+
+ def self.subtract(first, second)
+ first - second
+ end
+
+ def self.divide(first, second)
+ first / second
+ end
+
+ def self.mod(first, second)
+ first % second
+ end
+
+ def self.verify(string)
+ unless string.match(/^=([A-Z]+[\w\s,.=]+)$/) &&
+ string.count(",") == string.scan(/[\w.=]+/).size - 2
+ raise Error, "Invalid expression '#{string}'"
+ end
+ end
+
+ def self.verify_arguments(formula, args)
+ real_args = method(formula).arity
+ message = "Wrong number of arguments for '#{formula.upcase}': expected "
+ if real_args > 0 && real_args != args
+ raise Error, message + "#{real_args}, got #{args}"
+ elsif real_args < 0 && real_args.abs - 1 > args
+ raise Error, message + "at least #{real_args.abs - 1}, got #{args}"
+ end
+ end
+
+ # def self.method_missing(name, *args, &block)
+ # raise Error, "Unknown function '#{name.upcase}'"
+ # end
+
+ def self.send(name, *args, &block)
+ if methods.include?(name.to_sym)
+ verify_arguments(name, args.size)
+ super
+ elsif
+ raise Error, "Unknown function '#{name.upcase}'"
+ end
+ end
+ end
+end