Александър обнови решението на 09.11.2012 18:59 (преди около 12 години)
+class SexpVisitor
+ class SymbolicError < Exception
+ end
+
+ private
+
+ def self.visit(sexp, context=nil)
+ self.new(context).visit(sexp)
+ end
+
+ def self.handlers
+ @handlers ||= {}
+ end
+
+ def self.on(*types, &block)
+ @handlers ||= {}
+ types.each do |type|
+ @handlers[type] = block
+ end
+ end
+
+ public
+
+ def visit(sexp)
+ case sexp.first
+ when :+, :* then args = sexp
+ when :- then args = [sexp.last]
+ when :sin, :cos, :number, :variable then args = sexp
+ end
+ self.instance_exec *(args + [@environment || @variable]), &self.class.handlers[sexp.first]
+ end
+end
+
+class EvaluationVisitor < SexpVisitor
+ def initialize(environment = {})
+ @environment = environment
+ end
+
+ on :*, :+ do |operator, left, right|
+ visit(left).send(operator, visit(right))
+ end
+
+ on :variable do |_, variable, environment|
+ if environment.key? variable
+ environment[variable]
+ else
+ raise SymbolicError
+ end
+ end
+
+ on :number do |_, number|
+ number
+ end
+
+ on :- do |value|
+ - visit(value)
+ end
+
+ on :sin, :cos do |operation, value|
+ Math.send operation, visit(value)
+ end
+end
+
+class DerivationVisitor < SexpVisitor
+ def initialize(variable)
+ @variable = variable
+ end
+
+ on(:number) { [:number, 0] }
+
+ on :variable do |_, label, deriv_variable|
+ label == deriv_variable ? [:number, 1] : [:number, 0]
+ end
+
+ on(:+) { |_, left, right| [:+, visit(left), visit(right)] }
+
+ on :* do |_, left, right|
+ [:+, [:*, visit(left), right],
+ [:*, left, visit(right)]]
+ end
+
+ on(:-) { |value| [:-, visit(value)] }
+
+ on(:sin) { |_, value| [:cos, visit(value)] }
+
+ on(:cos) { |_, value| [:-, [:sin, visit(value)]] }
+end
+
+class SimplificationVisitor < SexpVisitor
+ def initialize(context=nil)
+ end
+
+ on :+ do |_, left_sexp, right_sexp|
+ left, right = visit(left_sexp), visit(right_sexp)
+ if left == [:number, 0]
+ right
+ elsif right == [:number, 0]
+ left
+ elsif left.first == :variable and right == [:-, left] or
+ right.first == :variable and left == [:-, right]
+ [:number, 0]
+ elsif left.first == :number and right.first == :number # no var
+ [:number, left.last + right.last]
+ else
+ [:+, left, right]
+ end
+ end
+
+ on :* do |_, left_sexp, right_sexp|
+ left, right = visit(left_sexp), visit(right_sexp)
+ if left == [:number, 0] or right == [:number, 0]
+ [:number, 0]
+ elsif left.first == :variable and right == [:-, left] or
+ right.first == :variable and left == [:-, right]
+ [:number, 0]
+ elsif left.last == 1
+ right
+ elsif right.last == 1
+ left
+ elsif left.first == :number and right.first == :number # no var
+ [:number, left.last + right.last]
+ else
+ [:*, left, right]
+ end
+ end
+
+ on :number, :variable do |type, value|
+ [type, value]
+ end
+
+ on :- do |value|
+ simplified = visit(value)
+ simplified.first == :number ? [:number, -simplified.last] : [:-, simplified]
+ end
+
+ on :sin, :cos do |operation, value|
+ simplified = visit(value)
+ if simplified.first == :number
+ [:number, Math.send(operation, simplified.last)]
+ else
+ [operation, simplified]
+ end
+ end
+end
+
+class Expr
+ attr_accessor :sexp
+
+ def self.build(sexp)
+ Expr.new(sexp)
+ end
+
+ def initialize(sexp)
+ @sexp = sexp
+ end
+
+ def ==(other)
+ @sexp == other.sexp
+ end
+
+ def evaluate(environment={})
+ EvaluationVisitor.visit(@sexp, environment)
+ end
+
+ def simplify
+ Expr.new(SimplificationVisitor.visit @sexp)
+ end
+
+ def derive(variable)
+ Expr.new(DerivationVisitor.visit(@sexp, variable)).simplify
+ end
+end