Станислав обнови решението на 13.11.2012 14:43 (преди почти 13 години)
+class Expr
+  def self.build(expr)
+    return expr if not expr.kind_of? Array
+    {
+      :number    =>  Number,
+      :variable  =>  Variable,
+      :+         =>  AdditionOperator,
+      :*         =>  MultiplicationOperator,
+      :-         =>  NegationOperator,
+      :sin       =>  Sine,
+      :cos       =>  Cosine,
+    }[expr[0]].new *expr[1..-1].map { |arg| Expr.build arg }
+  end
+
+  def ==(other)
+    self.class == other.class
+  end
+
+  def derive(variable)
+    derivative(variable).simplify
+  end
+end
+
+class Operand < Expr
+  attr_reader :value
+
+  def initialize(value)
+    @value = value
+  end
+
+  def ==(other)
+    super other and @value == other.value
+  end
+end
+
+class Variable < Operand
+  def evaluate(environment)
+    environment[@value]
+  end
+
+  def simplify
+    self.class.new @value
+  end
+
+  def derivative(variable)
+    Number.new @value == variable ? 1 : 0
+  end
+end
+
+class Number < Operand
+  def evaluate(environment)
+    @value
+  end
+
+  def simplify
+    self.class.new @value
+  end
+
+  def derivative(variable)
+    self.class.new 0
+  end
+end
+
+class Operator < Expr
+end
+
+class UnaryOperator < Operator
+  attr_reader :arg
+
+  def initialize(arg)
+    @arg = arg.simplify
+  end
+
+  def ==(other)
+    super other and @arg == other.arg
+  end
+end
+
+class BinaryOperator < Operator
+  attr_reader :first_arg, :second_arg
+
+  def initialize(first_arg, second_arg)
+    @first_arg, @second_arg = first_arg.simplify, second_arg.simplify
+  end
+
+  def ==(other)
+    super other and @first_arg == other.first_arg and @second_arg == other.second_arg
+  end
+end
+
+class Sine < UnaryOperator
+  def evaluate(environment)
+    Math.sin @arg.evaluate environment
+  end
+
+  def simplify
+    self.class.new @arg.simplify
+  end
+
+  def derivative(variable)
+    MultiplicationOperator.new Cosine.new(@arg), @arg.derive(variable)
+  end
+end
+
+class Cosine < UnaryOperator
+  def evaluate(environment)
+    Math.cos @arg.evaluate environment
+  end
+
+  def simplify
+    self.class.new @arg.simplify
+  end
+
+  def derivative(variable)
+    MultiplicationOperator.new NegationOperator.new(Sine.new @arg), @arg.derive(variable)
+  end
+end
+
+class NegationOperator < UnaryOperator
+  def evaluate(environment)
+    -@arg.evaluate(environment)
+  end
+
+  def simplify
+    self.class.new @arg.simplify
+  end
+
+  def derivative(variable)
+    self.class.new -@arg.derive(variable)
+  end
+end
+
+class AdditionOperator < BinaryOperator
+  def evaluate(environment)
+    @first_arg.evaluate(environment) + @second_arg.evaluate(environment)
+  end
+
+  def simplify
+    first_arg, second_arg = @first_arg.simplify, @second_arg.simplify
+    if first_arg.kind_of? Number and second_arg.kind_of? Number
+      Number.new first_arg.value + second_arg.value
+    elsif first_arg == Number.new(0)
+      second_arg
+    elsif second_arg == Number.new(0)
+      first_arg
+    else
+      self.class.new first_arg, second_arg
+    end
+  end
+
+  def derivative(variable)
+    self.class.new @first_arg.derive(variable), @second_arg.derive(variable)
+  end
+end
+
+class MultiplicationOperator < BinaryOperator
+  def evaluate(environment)
+    @first_arg.evaluate(environment) * @second_arg.evaluate(environment)
+  end
+
+  def simplify
+    first_arg, second_arg = @first_arg.simplify, @second_arg.simplify
+    if first_arg.kind_of?(Number) and second_arg.kind_of?(Number)
+      Number.new first_arg.value * second_arg.value
+    elsif first_arg == Number.new(0) or second_arg == Number.new(0)
+      Number.new 0
+    elsif first_arg == Number.new(1)
+      second_arg
+    elsif second_arg == Number.new(1)
+      first_arg
+    else
+      self.class.new first_arg, second_arg
+    end
+  end
+
+  def derivative(variable)
+    first_arg = self.class.new @first_arg.derive(variable), @second_arg
+    second_arg = self.class.new @first_arg, @second_arg.derive(variable)
+    AdditionOperator.new first_arg, second_arg
+  end
+end
