Кирчо обнови решението на 11.11.2012 18:03 (преди почти 13 години)
+class Expr
+  def self.build(expr_tree)
+    operation = expr_tree[0]
+    if expr_tree.length == 2
+      if operation == :number or operation == :variable
+        Unary.build operation, expr_tree[1]
+      else
+        Unary.build operation, build(expr_tree[1])
+      end
+    else
+      Binary.build operation, build(expr_tree[1]), build(expr_tree[2])
+    end
+  end
+end
+
+class Unary < Expr
+  def self.build(operation, argument)
+    case operation
+      when :sin      then Sine.new argument
+      when :cos      then Cosine.new argument
+      when :-        then Negation.new argument
+      when :number   then Number.new argument
+      when :variable then Variable.new argument
+    end
+  end
+end
+
+class Binary < Expr
+  def self.build(operation, left, right)
+    case operation
+      when :+ then Addition.new left, right
+      when :* then Multiplication.new left, right
+    end
+  end
+end
+
+class Addition < Binary
+  attr_reader :left, :right
+
+  def initialize(left, right)
+    @left = left
+    @right = right
+  end
+
+  def ==(other)
+    other.class == Addition and
+      left == other.left and
+      right == other.right
+  end
+
+  def evaluate(environment = {})
+    left.evaluate(environment) + right.evaluate(environment)
+  end
+
+  def exact?
+    left.exact? and right.exact?
+  end
+
+  def simplify
+    new_left, new_right = left.simplify, right.simplify
+
+    if    new_left.exact? and new_right.exact?
+      Number.new (new_left.evaluate + new_right.evaluate)
+    elsif new_left.exact? and new_left.evaluate == 0
+      new_right
+    elsif new_right.exact? and new_right.evaluate == 0
+      new_left
+    else
+      Addition.new new_left, new_right
+    end
+  end
+
+  def derive(variable)
+    new_left = left.derive variable
+    new_right = right.derive variable
+
+    (Addition.new new_left, new_right).simplify
+  end
+end
+
+class Multiplication < Binary
+  attr_reader :left, :right
+
+  def initialize(left, right)
+    @left = left
+    @right = right
+  end
+
+  def ==(other)
+    other.class == Multiplication and
+      left == other.left and
+      right == other.right
+  end
+
+  def evaluate(environment = {})
+    left.evaluate(environment) * right.evaluate(environment)
+  end
+
+  def exact?
+    left.exact? and right.exact?
+  end
+
+  def simplify
+    new_left, new_right = left.simplify, right.simplify
+
+    if    new_left.exact? and new_right.exact?
+      Number.new (new_left.evaluate * new_right.evaluate)
+    elsif new_left.exact? and (new_left.evaluate == 1 or new_left.evaluate == 0)
+      new_left.evaluate == 1 ? new_right : Number.new(0)
+    elsif new_right.exact? and (new_right.evaluate == 1 or new_right.evaluate == 0)
+      new_right.evaluate == 1 ? new_left : Number.new(0)
+    else
+      Multiplication.new new_left, new_right
+    end
+  end
+
+  def derive(variable)
+    new_left = left.derive variable
+    new_right = right.derive variable
+    left_mult = Multiplication.new new_left, right
+    right_mult = Multiplication.new left, new_right
+
+    (Addition.new left_mult, right_mult).simplify
+  end
+end
+
+class Number < Unary
+  attr_reader :argument
+
+  def initialize(argument)
+    @argument = argument
+  end
+
+  def ==(other)
+    other.class == Number and argument == other.argument
+  end
+
+  def evaluate(environment = {})
+    argument
+  end
+
+  def exact?
+    true
+  end
+
+  def simplify
+    dup
+  end
+
+  def derive(variable)
+    Number.new 0
+  end
+end
+
+class Variable < Unary
+  attr_reader :argument
+
+  def initialize(argument)
+    @argument = argument
+  end
+
+  def ==(other)
+    other.class == Variable and argument == other.argument
+  end
+
+  def evaluate(environment = {})
+    raise ArgumentError unless environment[argument]
+
+    environment[argument]
+  end
+
+  def exact?
+    false
+  end
+
+  def simplify
+    dup
+  end
+
+  def derive(variable)
+    argument == variable ? Number.new(1) : Number.new(0)
+  end
+end
+
+class Negation < Unary
+  attr_reader :argument
+
+  def initialize(argument)
+    @argument = argument
+  end
+
+  def ==(other)
+    other.class == Negation and argument == other.argument
+  end
+
+  def evaluate(environment = {})
+    -(argument.evaluate environment)
+  end
+
+  def exact?
+    argument.exact?
+  end
+
+  def simplify
+    new_argument = argument.simplify
+    if new_argument.exact?
+      Number.new (-new_argument.evaluate)
+    else
+      Negation.new new_argument
+    end
+  end
+
+  def derive(variable)
+    (Negation.new argument.derive(variable)).simplify
+  end
+end
+
+class Sine < Unary
+  attr_reader :argument
+
+  def initialize(argument)
+    @argument = argument
+  end
+
+  def ==(other)
+    other.class == Sine and argument == other.argument
+  end
+
+  def evaluate(environment = {})
+    Math.sin argument.evaluate(environment)
+  end
+
+  def exact?
+    argument.exact?
+  end
+
+  def simplify
+    new_argument = argument.simplify
+    if new_argument.exact?
+      Number.new (new_argument.evaluate)
+    else
+      Sine.new new_argument
+    end
+  end
+
+  def derive(variable)
+    argument_derivative = argument.derive variable
+
+    (Multiplication.new argument_derivative, Cosine.new(argument)).simplify
+  end
+end
+
+class Cosine < Unary
+  attr_reader :argument
+
+  def initialize(argument)
+    @argument = argument
+  end
+
+  def ==(other)
+    other.class == Cosine and argument == other.argument
+  end
+
+  def evaluate(environment = {})
+    Math.cos argument.evaluate(environment)
+  end
+
+  def exact?
+    argument.exact?
+  end
+
+  def simplify
+    new_argument = argument.simplify
+    if new_argument.exact?
+      Number.new (new_argument.evaluate)
+    else
+      Cosine.new new_argument
+    end
+  end
+
+  def derive(variable)
+    argument_derivative = argument.derive variable
+
+    (Multiplication.new argument_derivative, Negation.new(Sine.new(argument))).simplify
+  end
+end
