Камелия обнови решението на 11.11.2012 03:57 (преди почти 13 години)
+class Expr
+  def self.build(expr_tree)
+    if expr_tree.length == 3
+      Binary.build expr_tree
+    else
+      Unary.build expr_tree
+    end
+  end
+end
+
+class Binary < Expr
+  def self.build(expr_tree)
+    case expr_tree.shift
+    when :+ then Addition.new Expr.build(expr_tree.shift), Expr.build(expr_tree.shift)
+    when :* then Multiplication.new Expr.build(expr_tree.shift), Expr.build(expr_tree.shift)
+    end
+  end
+end
+
+class Addition < Binary
+  attr_reader :left, :right
+
+  def initialize(left, right)
+    @left, @right = left, right
+  end
+
+  def evaluate(environment = {})
+    (@left.evaluate environment) + (@right.evaluate environment)
+  end
+
+  def simplify
+    return Number.new evaluate if exact?
+    return @left.simplify if @right.exact? and @right.evaluate == 0
+    return @right.simplify if @left.exact? and @left.evaluate == 0
+    Addition.new @left.simplify, @right.simplify
+  end
+
+  def derive(variable)
+    Addition.new(@left.derive(variable), @right.derive(variable)).simplify
+  end
+
+  def exact?
+    @left.exact? and @right.exact?
+  end
+
+  def ==(other)
+    self.class == other.class and left == other.left and right == other.right
+  end
+end
+
+class Multiplication < Binary
+  attr_reader :left, :right
+
+  def initialize(left, right)
+    @left, @right = left, right
+  end
+
+  def evaluate(environment = {})
+    (@left.evaluate environment) * (@right.evaluate environment)
+  end
+
+  def simplify
+    return Number.new evaluate if exact?
+    if @left.exact?
+      return Number.new 0 if @left.evaluate == 0
+      return @right.simplify if @left.evaluate == 1
+    end
+    if @right.exact?
+      return Number.new 0 if @right.evaluate == 0
+      return @left.simplify if @right.evaluate == 1
+    end
+    Multiplication.new @left.simplify, @right.simplify
+  end
+
+  def derive(variable)
+    left = Multiplication.new @left.derive(variable), @right
+    right = Multiplication.new @right, @left.derive(variable)
+    Addition.new(left, right).simplify
+  end
+
+  def exact?
+    @left.exact? and @right.exact?
+  end
+
+  def ==(other)
+    self.class == other.class and left == other.left and right == other.right
+  end
+end
+
+class Unary < Expr
+  def self.build(expr_tree)
+    case expr_tree.shift
+    when :number then Number.new expr_tree.shift
+    when :variable then Variable.new expr_tree.shift
+    when :- then Negation.new Expr.build expr_tree.shift
+    when :sin then Sine.new Expr.build expr_tree.shift
+    when :cos then Cosine.new Expr.build expr_tree.shift
+    end
+  end
+end
+
+class Negation < Unary
+  attr_reader :arg
+
+  def initialize(arg)
+    @arg = arg
+  end
+
+  def evaluate(environment = {})
+    -(@arg.evaluate environment)
+  end
+
+  def simplify
+    return Number.new evaluate if exact?
+    Negation.new @arg.simplify
+  end
+
+  def derive(variable)
+    Negation.new(@arg.derive(variable)).simplify
+  end
+
+  def exact?
+    @arg.exact?
+  end
+
+  def ==(other)
+    self.class == other.class and arg == other.arg
+  end
+end
+
+class Sine < Unary
+  attr_reader :arg
+
+  def initialize(arg)
+    @arg = arg
+  end
+
+  def evaluate(environment = {})
+    Math.sin @arg.evaluate environment
+  end
+
+  def simplify
+    return Number.new evaluate if exact?
+    Sine.new @arg.simplify
+  end
+
+  def derive(variable)
+    Multiplication.new(@arg.derive(variable), Cosine.new(@arg)).simplify
+  end
+
+  def exact?
+    @arg.exact?
+  end
+
+  def ==(other)
+    self.class == other.class and arg == other.arg
+  end
+end
+
+class Cosine < Unary
+  attr_reader :arg
+
+  def initialize(arg)
+    @arg = arg
+  end
+
+  def evaluate(environment = {})
+    Math.cos @arg.evaluate environment
+  end
+
+  def simplify
+    return Number.new evaluate if exact?
+    Cosine.new @arg.simplify
+  end
+
+  def derive(variable)
+    Multiplication.new(@arg.derive(variable), Negation.new(Sine.new(@arg))).simplify
+  end
+
+  def exact?
+    @arg.exact?
+  end
+
+  def ==(other)
+    self.class == other.class and arg == other.arg
+  end
+end
+
+class Variable < Unary
+  attr_reader :arg
+
+  def initialize(arg)
+    @arg = arg
+  end
+
+  def evaluate(environment = {})
+    raise "uninitialized variable" if not environment.has_key? @arg
+    environment.fetch @arg
+  end
+
+  def simplify
+    self
+  end
+
+  def derive(variable)
+    return Number.new 1 if variable == @arg
+    return Number.new 0
+  end
+
+  def exact?
+    false
+  end
+
+  def ==(other)
+    self.class == other.class and arg == other.arg
+  end
+end
+
+class Number < Unary
+  attr_reader :arg
+
+  def initialize(arg)
+    @arg = arg
+  end
+
+  def evaluate(environment = {})
+    @arg
+  end
+
+  def simplify
+    self
+  end
+
+  def derive(variable)
+    Number.new 0
+  end
+
+  def exact?
+    true
+  end
+
+  def ==(other)
+    self.class == other.class and arg == other.arg
+  end
+end
