Решение на Осма задача от Здравко Андонов

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

Към профила на Здравко Андонов

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 37 успешни тест(а)
  • 3 неуспешни тест(а)

Код

class Spreadsheet
class Error < RuntimeError
end
def initialize(data_string = "")
@table = data_string.strip.split("\n")
.map { |row_string| row_string.strip.split(/\t| {2,}/) }
end
def empty?
@table.empty?
end
def cell_at(cell_index)
cell_index.match(/\A(?<column>[[:upper:]]+)(?<row>\d+)\Z/) do |match|
row, column = match["row"].to_i - 1, column_to_index(match["column"])
if row > @table.size or column > @table[0].size
raise Error, "Cell '#{cell_index}' does not exist"
else
return @table[row][column]
end
end
raise Error, "Invalid cell index '#{cell_index}'"
end
def [](cell_index)
cell_string = cell_at(cell_index)
evaluate(cell_string)
end
def to_s
@table.map { |row| row.map { |data| evaluate(data) }.join("\t") }.join("\n")
end
private
def column_to_index(column_string)
addends = column_string.each_char.map.with_index do |char, index|
(char.ord - 'A'.ord + 1) * 26**(column_string.size - index - 1)
end
addends.reduce(:+) - 1
end
def evaluate(cell_string)
if cell_string[0] == '='
Expression.new(cell_string[1..-1].lstrip, self).value
else
cell_string
end
end
class Functions
class << self
def add(arguments)
if arguments.size >= 2
arguments.reduce(:+)
else
message = "Wrong number of arguments for 'ADD':" +
" expected at least 2, got #{arguments.size}"
raise Error, message
end
end
def multiply(arguments)
if arguments.size >= 2
arguments.reduce(:*)
else
message = "Wrong number of arguments for 'MULTIPLY':" +
" expected at least 2, got #{arguments.size}"
raise Error, message
end
end
def subtract(arguments)
if arguments.size == 2
arguments[0] - arguments[1]
else
message = "Wrong number of arguments for 'SUBTRACT':" +
" expected 2, got #{arguments.size}"
raise Error, message
end
end
def divide(arguments)
if arguments.size == 2
arguments[0] / arguments[1]
else
message = "Wrong number of arguments for 'DIVIDE':" +
" expected 2, got #{arguments.size}"
raise Error, message
end
end
def mod(arguments)
if arguments.size == 2
arguments[0] % arguments[1]
else
message = "Wrong number of arguments for 'MOD':" +
" expected 2, got #{arguments.size}"
raise Error, message
end
end
end
end
class Expression
ARGUMENT = /((\d+(\.\d+)?)|([[:upper:]]+\d+))/
FUNCTION = /[[:upper:]]+\(#{ARGUMENT}(\s*,\s*#{ARGUMENT})*\)/
attr_accessor :value
def initialize(expression_string, spreadsheet)
@expression_string = expression_string
@spreadsheet = spreadsheet
result = calculate_expression(expression_string)
@value = format_number_to_string(result.to_f)
end
private
def calculate_expression(expression)
if /\A#{ARGUMENT}\Z/ =~ expression
calculate_argument(expression)
elsif /\A#{FUNCTION}\Z/ =~ expression
calculate_valid_function_from_string(expression)
else
raise Error, "Invalid expression '#{expression}'"
end
end
def calculate_argument(expression)
if expression =~ /\A\d/
expression.to_f
else
@spreadsheet[expression]
end
end
def calculate_valid_function_from_string(expression)
function = /(?<name>[[:upper:]]+)\((?<arguments>.+)\)/.match(expression)
calculate_function(function[:name], function[:arguments])
end
def calculate_function(function_name, arguments_string)
arguments = arguments_string.split(/\s*,\s*/)
.map { |argument| calculate_argument(argument).to_f }
Functions.public_send(function_name.downcase.to_sym, arguments)
rescue NoMethodError
raise Error, "Unknown function '#{function_name}'"
end
def whole_number?(float)
float.to_i == float
end
def format_number_to_string(float)
if whole_number?(float)
float.to_i.to_s
else
sprintf("%.2f", float.round(2))
end
end
end
end

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

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

Failures:

  1) Spreadsheet#[] raises an exception for non-existant cells
     Failure/Error: expect { Spreadsheet.new()['A1'] }.to raise_error(Spreadsheet::Error, /Cell 'A1' does not exist/)
       expected Spreadsheet::Error with message matching /Cell 'A1' does not exist/, got #<NoMethodError: undefined method `size' for nil:NilClass> with backtrace:
         # /tmp/d20160121-5693-1jb5eus/solution.rb:17:in `block in cell_at'
         # /tmp/d20160121-5693-1jb5eus/solution.rb:15:in `match'
         # /tmp/d20160121-5693-1jb5eus/solution.rb:15:in `cell_at'
         # /tmp/d20160121-5693-1jb5eus/solution.rb:28:in `[]'
         # /tmp/d20160121-5693-1jb5eus/spec.rb:75:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1jb5eus/spec.rb:75: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-1jb5eus/spec.rb:75: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()')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'ADD': expected at least 2, got 0/, got #<Spreadsheet::Error: Invalid expression 'ADD()'> with backtrace:
         # /tmp/d20160121-5693-1jb5eus/solution.rb:128:in `calculate_expression'
         # /tmp/d20160121-5693-1jb5eus/solution.rb:116:in `initialize'
         # /tmp/d20160121-5693-1jb5eus/solution.rb:47:in `new'
         # /tmp/d20160121-5693-1jb5eus/solution.rb:47:in `evaluate'
         # /tmp/d20160121-5693-1jb5eus/solution.rb:29:in `[]'
         # /tmp/d20160121-5693-1jb5eus/spec.rb:123:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1jb5eus/spec.rb:123: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-1jb5eus/spec.rb:123: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()')['A1'] }.to raise_error(
       expected Spreadsheet::Error with message matching /Wrong number of arguments for 'MULTIPLY': expected at least 2, got 0/, got #<Spreadsheet::Error: Invalid expression 'MULTIPLY()'> with backtrace:
         # /tmp/d20160121-5693-1jb5eus/solution.rb:128:in `calculate_expression'
         # /tmp/d20160121-5693-1jb5eus/solution.rb:116:in `initialize'
         # /tmp/d20160121-5693-1jb5eus/solution.rb:47:in `new'
         # /tmp/d20160121-5693-1jb5eus/solution.rb:47:in `evaluate'
         # /tmp/d20160121-5693-1jb5eus/solution.rb:29:in `[]'
         # /tmp/d20160121-5693-1jb5eus/spec.rb:153:in `block (4 levels) in <top (required)>'
         # /tmp/d20160121-5693-1jb5eus/spec.rb:153: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-1jb5eus/spec.rb:153: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.05955 seconds
40 examples, 3 failures

Failed examples:

rspec /tmp/d20160121-5693-1jb5eus/spec.rb:74 # Spreadsheet#[] raises an exception for non-existant cells
rspec /tmp/d20160121-5693-1jb5eus/spec.rb:118 # Spreadsheet#[] raises an exception for less than two arguments passed to ADD
rspec /tmp/d20160121-5693-1jb5eus/spec.rb:148 # Spreadsheet#[] raises an exception for less than two arguments to MULTIPLY

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

Здравко обнови решението на 09.01.2016 21:31 (преди около 9 години)

+class Spreadsheet
+ class Error < RuntimeError
+ end
+
+ def initialize(data_string = "")
+ @table = data_string.strip.split("\n")
+ .map { |row_string| row_string.strip.split(/\t| {2,}/) }
+ end
+
+ def empty?
+ @table.empty?
+ end
+
+ def cell_at(cell_index)
+ cell_index.match(/\A(?<column>[[:upper:]]+)(?<row>\d+)\Z/) do |match|
+ row, column = match["row"].to_i - 1, column_to_index(match["column"])
+ if row > @table.size or column > @table[0].size
+ raise Error, "Cell '#{cell_index}' does not exist"
+ else
+ return @table[row][column]
+ end
+ end
+
+ raise Error, "Invalid cell index '#{cell_index}'"
+ end
+
+ def [](cell_index)
+ cell_string = cell_at(cell_index)
+ evaluate(cell_string)
+ end
+
+ def to_s
+ @table.map { |row| row.map { |data| evaluate(data) }.join("\t") }.join("\n")
+ end
+
+ private
+
+ def column_to_index(column_string)
+ addends = column_string.each_char.map.with_index do |char, index|
+ (char.ord - 'A'.ord + 1) * 26**(column_string.size - index - 1)
+ end
+ addends.reduce(:+) - 1
+ end
+
+ def evaluate(cell_string)
+ if cell_string[0] == '='
+ Expression.new(cell_string[1..-1].lstrip, self).value
+ else
+ cell_string
+ end
+ end
+
+ class Functions
+ class << self
+ def add(arguments)
+ if arguments.size >= 2
+ arguments.reduce(:+)
+ else
+ message = "Wrong number of arguments for 'ADD':" +
+ " expected at least 2, got #{arguments.size}"
+ raise Error, message
+ end
+ end
+
+ def multiply(arguments)
+ if arguments.size >= 2
+ arguments.reduce(:*)
+ else
+ message = "Wrong number of arguments for 'MULTIPLY':" +
+ " expected at least 2, got #{arguments.size}"
+ raise Error, message
+ end
+ end
+
+ def subtract(arguments)
+ if arguments.size == 2
+ arguments[0] - arguments[1]
+ else
+ message = "Wrong number of arguments for 'SUBTRACT':" +
+ " expected 2, got #{arguments.size}"
+ raise Error, message
+ end
+ end
+
+ def divide(arguments)
+ if arguments.size == 2
+ arguments[0] / arguments[1]
+ else
+ message = "Wrong number of arguments for 'DIVIDE':" +
+ " expected 2, got #{arguments.size}"
+ raise Error, message
+ end
+ end
+
+ def mod(arguments)
+ if arguments.size == 2
+ arguments[0] % arguments[1]
+ else
+ message = "Wrong number of arguments for 'MOD':" +
+ " expected 2, got #{arguments.size}"
+ raise Error, message
+ end
+ end
+ end
+ end
+
+ class Expression
+ ARGUMENT = /((\d+(\.\d+)?)|([[:upper:]]+\d+))/
+ FUNCTION = /[[:upper:]]+\(#{ARGUMENT}(\s*,\s*#{ARGUMENT})*\)/
+
+ attr_accessor :value
+
+ def initialize(expression_string, spreadsheet)
+ @expression_string = expression_string
+ @spreadsheet = spreadsheet
+ result = calculate_expression(expression_string)
+ @value = format_number_to_string(result.to_f)
+ end
+
+ private
+
+ def calculate_expression(expression)
+ if /\A#{ARGUMENT}\Z/ =~ expression
+ calculate_argument(expression)
+ elsif /\A#{FUNCTION}\Z/ =~ expression
+ calculate_valid_function_from_string(expression)
+ else
+ raise Error, "Invalid expression '#{expression}'"
+ end
+ end
+
+ def calculate_argument(expression)
+ if expression =~ /\A\d/
+ expression.to_f
+ else
+ @spreadsheet[expression]
+ end
+ end
+
+ def calculate_valid_function_from_string(expression)
+ function = /(?<name>[[:upper:]]+)\((?<arguments>.+)\)/.match(expression)
+ calculate_function(function[:name], function[:arguments])
+ end
+
+ def calculate_function(function_name, arguments_string)
+ arguments = arguments_string.split(/\s*,\s*/)
+ .map { |argument| calculate_argument(argument).to_f }
+ Functions.public_send(function_name.downcase.to_sym, arguments)
+ rescue NoMethodError
+ raise Error, "Unknown function '#{function_name}'"
+ end
+
+ def whole_number?(float)
+ float.to_i == float
+ end
+
+ def format_number_to_string(float)
+ if whole_number?(float)
+ float.to_i.to_s
+ else
+ sprintf("%.2f", float.round(2))
+ end
+ end
+ end
+end