Гергана обнови решението на 12.11.2012 01:21 (преди около 12 години)
+class Expr
+
+ def Expr.build(expr_tree)
+ case expr_tree.first
+ when :number then Number.new(expr_tree[1])
+ when :variable then Variable.new(expr_tree[1])
+ when :+ then Addition.new(Expr.build(expr_tree[1]), Expr.build(expr_tree[2]))
+ when :* then Multiplication.new(Expr.build(expr_tree[1]), Expr.build(expr_tree[2]))
+ when :- then Negation.new(Expr.build(expr_tree[1]))
+ when :sin then Sine.new(Expr.build(expr_tree[1]))
+ when :cos then Cosine.new(Expr.build(expr_tree[1]))
+ end
+ end
+
+ def +(other_expr)
+ Addition.new(self.simplify, other_expr.simplify)
+ end
+
+ def *(other_expr)
+ Multiplication.new(self.simplify, other_expr.simplify)
+ end
+
+ def -@
+ Negation.new(self.simplify)
+ end
+end
+
+class Unary < Expr
+ attr_accessor :argument
+
+ def initialize(argument)
+ @argument = argument
+ end
+
+ def ==(other_expr)
+ @argument == other_expr.argument
+ end
+end
+
+class Binary < Expr
+ attr_accessor :arguments
+
+ def initialize(*arguments)
+ @arguments = arguments
+ end
+
+ def ==(other_expr)
+ @arguments == other_expr.arguments
+ end
+
+ def exact?
+ @arguments[0].exact? and @arguments[1].exact?
+ end
+end
+
+class Number < Unary
+
+ def evaluate(*_)
+ @argument
+ end
+
+ def derive(*_)
+ 0
+ end
+
+ def exact?
+ true
+ end
+
+ def simplify
+ self
+ end
+end
+
+class Variable < Unary
+
+ def evaluate(environment = {})
+ environment[@argument] or raise "Missing value for #{@argument} in environment"
+ end
+
+ def derive(variable)
+ @argument == variable ? Number.new(1) : Number.new(0)
+ end
+
+ def exact?
+ false
+ end
+
+ def simplify
+ self
+ end
+end
+
+class Addition < Binary
+
+ def evaluate(environment = {})
+ @arguments.map { |argument| argument.evaluate(environment) }.inject(:+)
+ end
+
+ def derive(variable)
+ @arguments.map { |argument| argument.derive(variable) }.inject(:+).simplify
+ end
+
+ def simplify
+ first_argument, second_argument = @arguments.map { |argument| argument.simplify }
+ if first_argument.exact? and second_argument.exact?
+ Number.new(first_argument.evaluate + second_argument.evaluate)
+ elsif first_argument.exact? and first_argument.evaluate == 0 then second_argument
+ elsif second_argument.exact? and second_argument.evaluate == 0 then first_argument
+ else self
+ end
+ end
+end
+
+class Multiplication < Binary
+
+ def evaluate(environment = {})
+ @arguments.map { |argument| argument.evaluate(environment) }.inject(:*)
+ end
+
+ def derive(variable)
+ first_addend = @arguments[0].derive(variable) * @arguments[1]
+ second_addend = @arguments[0] * @arguments[1].derive(variable)
+ first_addend.simplify + second_addend.simplify
+ end
+
+ def simplify
+ first_argument, second_argument = @arguments.map { |argument| argument.simplify }
+ if first_argument.exact? and second_argument.exact?
+ Number.new(first_argument.evaluate * second_argument.evaluate)
+ elsif first_argument.exact? and first_argument.evaluate == 0 then Number.new 0
+ elsif second_argument.exact? and second_argument.evaluate == 0 then Number.new 0
+ elsif first_argument.exact? and first_argument.evaluate == 1 then second_argument
+ elsif second_argument.exact? and second_argument.evaluate == 1 then first_argument
+ else self
+ end
+ end
+end
+
+class Negation < Unary
+
+ def evaluate(environment = {})
+ -@argument.evaluate(environment)
+ end
+
+ def derive(variable)
+ -@argument.derive(variable).simplify
+ end
+
+ def exact?
+ @argument.exact?
+ end
+
+ def simplify
+ @argument.simplify
+ end
+end
+
+class Sine < Unary
+
+ def evaluate(environment = {})
+ Math.sin @argument.evaluate(environment)
+ end
+
+ def derive(variable)
+ @argument.derive(variable) * Math.cos(@argument).simplify
+ end
+
+ def exact?
+ @argument.exact?
+ end
+
+ def simplify
+ argument = @argument.simplify
+ if argument.exact?
+ Math.sin argument.evaluate
+ else
+ Sine.new argument
+ end
+ end
+end
+
+class Cosine < Unary
+
+ def evaluate(environment = {})
+ Math.cos @argument.evaluate(environment)
+ end
+
+ def derive(variable)
+ @argument.derive(variable) * - Math.sin(@argument)
+ end
+
+ def exact?
+ @argument.exact?
+ end
+
+ def simplify
+ argument = @argument.simplify
+ if argument.exact?
+ Math.cos argument.evaluate
+ else
+ Cosine.new argument
+ end
+ end
+end