Петър обнови решението на 13.11.2012 01:18 (преди около 12 години)
+class Expr
+ attr_reader :expression
+
+ def self.build(expression)
+ new ExpressionFactory.build(expression)
+ end
+
+ def initialize(expression)
+ @expression = expression
+ end
+
+ def evaluate(env = {})
+ visitor = EvaluateVisitor.new env
+ @expression.accept visitor
+ visitor.execute
+ end
+
+ def simplify
+ visitor = SimplifyVisitor.new
+ @expression.accept visitor
+ visitor.execute
+ end
+
+ def derive(variable)
+ visitor = DeriveVisitor.new variable
+ @expression.accept visitor
+ visitor.execute
+ end
+
+ def ==(other)
+ @expression == other.expression
+ end
+
+ def exact?
+ @expression.exact?
+ end
+end
+
+class ExpressionFactory
+ def self.build(expression)
+ case expression.first
+ when :variable then Variable.new expression[1]
+ when :number then Number.new expression[1]
+ when :- then Negation.new build(expression[1])
+ when :sin then Sine.new build(expression[1])
+ when :cos then Cosine.new build(expression[1])
+ when :+ then Addition.new build(expression[1]), build(expression[2])
+ when :* then Multiplication.new build(expression[1]), build(expression[2])
+ end
+ end
+end
+
+class Expression
+ def accept
+ end
+
+ def ==(other)
+ self.to_s == other.to_s
+ end
+
+ def to_s
+ end
+end
+
+class Binary < Expression
+ attr_reader :left_expression, :right_expression
+
+ def initialize left_expression, right_expression
+ @left_expression = left_expression
+ @right_expression = right_expression
+ end
+
+ def exact?
+ as_array.any? { |expr| expr.exact? }
+ end
+
+ def as_array
+ [@left_expression, @right_expression]
+ end
+end
+
+class Multiplication < Binary
+ def initialize(left_expression, right_expression)
+ super left_expression, right_expression
+ end
+
+ def accept(visitor)
+ visitor.visit_multiplication self
+ end
+
+ def to_s
+ "#{@left_expression.to_s} * #{@right_expression.to_s}"
+ end
+end
+
+class Addition < Binary
+ def initialize(left_expression, right_expression)
+ super left_expression, right_expression
+ end
+
+ def accept(visitor)
+ visitor.visit_addition self
+ end
+
+ def to_s
+ "#{@left_expression.to_s} + #{@right_expression.to_s}"
+ end
+end
+
+class Unary < Expression
+ attr_reader :inner
+
+ def initialize(expression)
+ @inner = expression
+ end
+
+ def exact?
+ @inner.exact?
+ end
+end
+
+class Sine < Unary
+ def initialize(expression)
+ super expression
+ end
+
+ def accept(visitor)
+ visitor.visit_sine self
+ end
+
+ def to_s
+ "sin(#{@inner.to_s})"
+ end
+end
+
+class Cosine < Unary
+ def initialize(expression)
+ super expression
+ end
+
+ def accept(visitor)
+ visitor.visit_cosine self
+ end
+
+ def to_s
+ "cos(#{@inner.to_s})"
+ end
+end
+
+class Negation < Unary
+ def initialize(expression)
+ super expression
+ end
+
+ def accept(visitor)
+ visitor.visit_negation self
+ end
+
+ def to_s
+ "-#{@inner.to_s}"
+ end
+end
+
+class Number < Unary
+ attr_reader :value
+
+ def initialize(value)
+ @value = value
+ end
+
+ def accept(visitor)
+ visitor.visit_number self
+ end
+
+ def to_s
+ @value.to_s
+ end
+
+ def exact?
+ false
+ end
+end
+
+class Variable < Unary
+ attr_reader :value
+
+ def initialize(value)
+ @value = value
+ end
+
+ def accept(visitor)
+ visitor.visit_variable self
+ end
+
+ def to_s
+ @value.to_s
+ end
+
+ def exact?
+ true
+ end
+end
+
+module VisitorBase
+ private
+ def visit(expression, type)
+ @expression = expression
+ @type = type
+ end
+
+ def visitor_instance(expression)
+ if @additional_params != nil
+ visitor = self.class.new(@additional_params)
+ else
+ visitor = self.class.new
+ end
+ expression.accept visitor
+ visitor
+ end
+end
+
+class Visitor
+ include VisitorBase
+
+ def visit_number(number)
+ visit number, :number
+ end
+
+ def visit_variable(variable)
+ visit variable, :variable
+ end
+
+ def visit_addition(expression)
+ visit expression, :addition
+ end
+
+ def visit_multiplication(expression)
+ visit expression, :multiplication
+ end
+
+ def visit_negation(expression)
+ visit expression, :negation
+ end
+
+ def visit_sine(expression)
+ visit expression, :sine
+ end
+
+ def visit_cosine(expression)
+ visit expression, :cosine
+ end
+
+ def execute
+ case @type
+ when :number then public_send @method_name + "_number"
+ when :variable then public_send @method_name + "_variable"
+ when :addition then public_send @method_name + "_addition"
+ when :multiplication then public_send @method_name + "_multiplication"
+ when :negation then public_send @method_name + "_negation"
+ when :sine then public_send @method_name + "_sine"
+ when :cosine then public_send @method_name + "_cosine"
+ end
+ end
+end
+
+class EvaluateVisitor < Visitor
+ def initialize(env = {})
+ @additional_params = env
+ @method_name = "evaluate"
+ end
+
+ def evaluate_number
+ @expression.value
+ end
+
+ def evaluate_variable
+ @additional_params[@expression.value]
+ end
+
+ def evaluate_addition
+ @expression.as_array.inject(0) { |sum, expr| sum += visitor_instance(expr).execute }
+ end
+
+ def evaluate_multiplication
+ @expression.as_array.inject(1) { |prod, expr| prod *= visitor_instance(expr).execute }
+ end
+
+ def evaluate_negation
+ visitor = visitor_instance @expression.inner
+ -1 * visitor.execute
+ end
+
+ def evaluate_sine
+ visitor = visitor_instance @expression.inner
+ Math.sin visitor.execute
+ end
+
+ def evaluate_cosine
+ visitor = visitor_instance @expression.inner
+ Math.cos visitor.execute
+ end
+end
+
+class SimplifyVisitor < Visitor
+ def initialize
+ @method_name = "simplify"
+ end
+
+ def simplify_number
+ Expr.new @expression
+ end
+
+ def simplify_variable
+ Expr.new @expression
+ end
+
+ def simplify_addition
+ simplified = @expression.as_array.map { |expr| visitor_instance(expr).execute }
+ if simplified.none?(&:exact?)
+ Expr.new Number.new simplified.first.evaluate + simplified.last.evaluate
+ elsif simplified.all?(&:exact?)
+ Expr.new Addition.new simplified.first.expression, simplified.last.expression
+ elsif simplified.any? { |expr| not expr.exact? and expr.evaluate == 0 }
+ simplified.select(&:exact?).first
+ else
+ Expr.new Addition.new simplified.first.expression, simplified.last.expression
+ end
+ end
+
+ def simplify_multiplication
+ simplified = @expression.as_array.map { |expr| visitor_instance(expr).execute }
+ if simplified.none?(&:exact?)
+ Expr.new Number.new simplified.first.evaluate * simplified.last.evaluate
+ elsif simplified.all?(&:exact?)
+ Expr.new Multiplication.new simplified.first.expression, simplified.last.expression
+ elsif simplified.any? { |expr| not expr.exact? and expr.evaluate == 1 }
+ simplified.select(&:exact?).first
+ elsif simplified.any? { |expr| not expr.exact? and expr.evaluate == 0 }
+ Expr.new Number.new 0
+ else
+ Expr.new Multiplication.new simplified.first.expression, simplified.last.expression
+ end
+ end
+
+ def simplify_negation
+ Expr.new @expression
+ end
+
+ def simplify_sine
+ simplified = visitor_instance(@expression.inner).execute
+ if simplified.exact?
+ Expr.new Sine.new simplified.expression
+ else
+ Expr.new Math.sin simplified.evaluate
+ end
+ end
+
+ def simplify_cosine
+ simplified = visitor_instance(@expression.inner).execute
+ if simplified.exact?
+ Expr.new Cosine.new simplified.expression
+ else
+ Expr.new Math.cos simplified.evaluate
+ end
+ end
+end
+
+class DeriveVisitor < Visitor
+ def initialize variable
+ @method_name = "derive"
+ @additional_params = variable
+ end
+
+ def derive_number
+ Expr.new Number.new 0
+ end
+
+ def derive_variable
+ if @additional_params == @expression.value
+ Expr.new Number.new 1
+ else
+ Expr.new Number.new 0
+ end
+ end
+
+ def derive_addition
+ derived = @expression.as_array.map { |expr| visitor_instance(expr).execute }
+ Expr.new(Addition.new derived.first.expression, derived.last.expression).simplify
+ end
+
+ def derive_multiplication
+ derived = @expression.as_array.map { |expr| visitor_instance(expr).execute }
+ left = Multiplication.new derived.first.expression, @expression.right_expression
+ right = Multiplication.new @expression.left_expression, derived.last.expression
+ Expr.new(Addition.new left, right).simplify
+ end
+
+ def derive_sine
+ derived = visitor_instance(@expression.inner).execute
+ cosine = Cosine.new @expression.inner
+ Expr.new(Multiplication.new derived.expression, cosine).simplify
+ end
+
+ def derive_cosine
+ derived = visitor_instance(@expression.inner).execute
+ sine = Sine.new @expression.inner
+ Expr.new(Multiplication.new derived.expression, Negation.new(sine)).simplify
+ end
+end
Скромен опит за реализиране на Visitor pattern за задачата. :) Не се получи точно както очаквах. Visitor класовете станаха тежки, а и ми се налагаше да се боря с ограниченията в задачата(за това свидетелства един модул, който съм ползвал :)). Евентуално може да реализирам и другия вариант утре. :)