Решение на Трета задача от Александър Иванов

Обратно към всички решения

Към профила на Александър Иванов

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 13 успешни тест(а)
  • 0 неуспешни тест(а)

Код

module ShortcutAccessors
def any
proc { true }
end
def left
proc { @left }
end
def right
proc { @right }
end
def value
proc { @value }
end
end
class PatternVisitor
include ShortcutAccessors
class SymbolicError < Exception
end
def self.visit(sexp, context=nil)
context ? self.new(context).visit(sexp) : self.new.visit(sexp)
end
def self.rules(&patterns)
@patterns = patterns
end
def apply_procs(expression)
expression.map do |element|
if element.is_a?(Array)
apply_procs(expression)
elsif element.is_a?(Proc)
element.call
else
element
end
end
end
def pattern_match?(pattern, sexp)
if pattern.is_a?(Array) and
sexp.is_a?(Array) and pattern.zip(sexp).all? do |sub_pattern, sub_sexp|
pattern_match? sub_pattern, sub_sexp
end
true
elsif pattern.is_a?(Proc) and pattern.call(sexp)
true
elsif (pattern == sexp) or (sexp.is_a?(Array) and sexp[0] == pattern)
true
end
end
def match(side)
proc { |sexp| self.instance_variable_get("@#{side}") == sexp }
end
def patternize(*args)
@patterns = self.instance_exec &self.class.instance_variable_get("@patterns")
end
public
def visit(sexp, depth=0)
return sexp if not sexp.is_a?(Array)
if sexp.length == 3
@left, @right = sexp[1..-1] = @dont_visit_args ?
sexp[1..-1] :
[visit(sexp[1], depth + 1), visit(sexp[2], depth + 1)]
else
@value = sexp[1] = (@dont_visit_args ? sexp[1] : visit(sexp[1], depth + 1))
end
pat, expression = (@patterns.find{ |pattern, _| pattern_match? pattern, sexp })
expression.is_a?(Proc) ? expression.call(*[sexp[1..-1]]) : apply_procs(expression)
end
end
class Evaluator < PatternVisitor
def initialize(environment={})
@environment = environment
@action = :evaluate
patternize
end
rules do
{
[:number, any] => value,
[:variable, any] => proc { @environment.fetch(value.call) },
[:sin, any] => proc { Math.sin value.call },
[:cos, any] => proc { Math.cos value.call },
[:-, any] => proc { -value.call },
[:+, any, any] => proc { left.call + right.call },
[:*, any, any] => proc { left.call * right.call }
}
end
end
class Simplifier < PatternVisitor
def initialize
@action = :simplify
patternize
end
rules do
{
[:*, [:number, 0], any] => [:number, 0],
[:*, any, [:number, 0]] => [:number, 0],
[:*, [:number, 1], any] => right,
[:*, any, [:number, 1]] => left,
[:*, :number, :number] => proc { [:number, left.call.last * right.call.last] },
[:*, any, any] => [:*, left, right],
[:+, [:number, 0], any] => right,
[:+, any, [:number, 0]] => left,
[:+, :variable, [:-, match(:left)]] => [:number, 0],
[:+, [:-, match(:right)], :variable] => [:number, 0],
[:+, :number, :number] => proc { [:number, left.call.last + right.call.last] },
[:+, any, any] => [:+, left, right],
[:-, :number] => proc { [:number, -value.call.last] },
[:-, any] => [:-, value],
[:number, any] => [:number, value],
[:variable, any] => [:variable, value],
[:sin, [:number, any]] => proc { Math.sin(value.call.last) },
[:sin, any] => [:sin, value],
[:cos, [:number, any]] => proc { Math.cos(value.call.last) },
[:cos, any] => [:cos, value]
}
end
end
class Derivator < PatternVisitor
def initialize(argument=:x)
@argument = argument
@action = :derive
@dont_visit_args = true
patternize
end
rules do
{
[:number, any] => [:number, 0],
[:variable, @argument] => [:number, 1],
[:variable, any] => [:number, 0],
[:-, any] => proc { |value| [:-, visit(value)] },
[:sin, any] => proc { |value| [:*, visit(value), [:cos, value]] },
[:cos, any] => proc { |value| [:*, visit(value), [:-, [:sin, value]]] },
[:+, any, any] => proc { |left, right| [:+, visit(left), visit(right)] },
[:*, any, any] => proc do |left, right|
[:+, [:*, visit(left), right], [:*, left, visit(right)]]
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={})
Evaluator.visit(@sexp, environment)
end
def simplify
Expr.new(Simplifier.visit @sexp)
end
def derive(variable)
Expr.new(Derivator.visit(@sexp, variable)).simplify
end
end

Лог от изпълнението

.............

Finished in 0.08 seconds
13 examples, 0 failures

История (3 версии и 1 коментар)

Александър обнови решението на 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

Александър обнови решението на 09.11.2012 19:01 (преди около 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)]
+ [:number, Math.send(operation, simplified.last)]
else
- [operation, simplified]
+ [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

(бе за 1 вариант на решението) Приемливо ли е да използвам това подобие на dsl във Visitor-класовете , със създаване на блокове в тялото на класа като по-гъвкав вариант на методи(а и не съм сигурен, че се очаква да минава от гледна точка на skeptic)

Александър обнови решението на 14.11.2012 12:22 (преди около 12 години)

-class SexpVisitor
- class SymbolicError < Exception
+module ShortcutAccessors
+ def any
+ proc { true }
end
- private
+ def left
+ proc { @left }
+ end
- def self.visit(sexp, context=nil)
- self.new(context).visit(sexp)
+ def right
+ proc { @right }
end
- def self.handlers
- @handlers ||= {}
+ def value
+ proc { @value }
end
+end
- def self.on(*types, &block)
- @handlers ||= {}
- types.each do |type|
- @handlers[type] = block
- end
+class PatternVisitor
+ include ShortcutAccessors
+ class SymbolicError < Exception
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]
+ def self.visit(sexp, context=nil)
+ context ? self.new(context).visit(sexp) : self.new.visit(sexp)
end
-end
-class EvaluationVisitor < SexpVisitor
- def initialize(environment = {})
- @environment = environment
+ def self.rules(&patterns)
+ @patterns = patterns
end
- on :*, :+ do |operator, left, right|
- visit(left).send(operator, visit(right))
+ def apply_procs(expression)
+ expression.map do |element|
+ if element.is_a?(Array)
+ apply_procs(expression)
+ elsif element.is_a?(Proc)
+ element.call
+ else
+ element
+ end
+ end
end
- on :variable do |_, variable, environment|
- if environment.key? variable
- environment[variable]
- else
- raise SymbolicError
+ def pattern_match?(pattern, sexp)
+ if pattern.is_a?(Array) and
+ sexp.is_a?(Array) and pattern.zip(sexp).all? do |sub_pattern, sub_sexp|
+ pattern_match? sub_pattern, sub_sexp
+ end
+ true
+ elsif pattern.is_a?(Proc) and pattern.call(sexp)
+ true
+ elsif (pattern == sexp) or (sexp.is_a?(Array) and sexp[0] == pattern)
+ true
end
end
- on :number do |_, number|
- number
+ def match(side)
+ proc { |sexp| self.instance_variable_get("@#{side}") == sexp }
end
- on :- do |value|
- - visit(value)
+ def patternize(*args)
+ @patterns = self.instance_exec &self.class.instance_variable_get("@patterns")
end
- on :sin, :cos do |operation, value|
- Math.send operation, visit(value)
+ public
+ def visit(sexp, depth=0)
+ return sexp if not sexp.is_a?(Array)
+ if sexp.length == 3
+ @left, @right = sexp[1..-1] = @dont_visit_args ?
+ sexp[1..-1] :
+ [visit(sexp[1], depth + 1), visit(sexp[2], depth + 1)]
+ else
+ @value = sexp[1] = (@dont_visit_args ? sexp[1] : visit(sexp[1], depth + 1))
+ end
+ pat, expression = (@patterns.find{ |pattern, _| pattern_match? pattern, sexp })
+ expression.is_a?(Proc) ? expression.call(*[sexp[1..-1]]) : apply_procs(expression)
end
end
-class DerivationVisitor < SexpVisitor
- def initialize(variable)
- @variable = variable
+class Evaluator < PatternVisitor
+ def initialize(environment={})
+ @environment = environment
+ @action = :evaluate
+ patternize
end
- on(:number) { [:number, 0] }
-
- on :variable do |_, label, deriv_variable|
- label == deriv_variable ? [:number, 1] : [:number, 0]
+ rules do
+ {
+ [:number, any] => value,
+ [:variable, any] => proc { @environment.fetch(value.call) },
+ [:sin, any] => proc { Math.sin value.call },
+ [:cos, any] => proc { Math.cos value.call },
+ [:-, any] => proc { -value.call },
+ [:+, any, any] => proc { left.call + right.call },
+ [:*, any, any] => proc { left.call * right.call }
+ }
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)
+class Simplifier < PatternVisitor
+ def initialize
+ @action = :simplify
+ patternize
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
+ rules do
+ {
+ [:*, [:number, 0], any] => [:number, 0],
+ [:*, any, [:number, 0]] => [:number, 0],
+ [:*, [:number, 1], any] => right,
+ [:*, any, [:number, 1]] => left,
+ [:*, :number, :number] => proc { [:number, left.call.last * right.call.last] },
+ [:*, any, any] => [:*, left, right],
+ [:+, [:number, 0], any] => right,
+ [:+, any, [:number, 0]] => left,
+ [:+, :variable, [:-, match(:left)]] => [:number, 0],
+ [:+, [:-, match(:right)], :variable] => [:number, 0],
+ [:+, :number, :number] => proc { [:number, left.call.last + right.call.last] },
+ [:+, any, any] => [:+, left, right],
+ [:-, :number] => proc { [:number, -value.call.last] },
+ [:-, any] => [:-, value],
+ [:number, any] => [:number, value],
+ [:variable, any] => [:variable, value],
+ [:sin, [:number, any]] => proc { Math.sin(value.call.last) },
+ [:sin, any] => [:sin, value],
+ [:cos, [:number, any]] => proc { Math.cos(value.call.last) },
+ [:cos, any] => [:cos, value]
+ }
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
+class Derivator < PatternVisitor
+ def initialize(argument=:x)
+ @argument = argument
+ @action = :derive
+ @dont_visit_args = true
+ patternize
end
- on :number, :variable do |type, value|
- [type, value]
+ rules do
+ {
+ [:number, any] => [:number, 0],
+ [:variable, @argument] => [:number, 1],
+ [:variable, any] => [:number, 0],
+ [:-, any] => proc { |value| [:-, visit(value)] },
+ [:sin, any] => proc { |value| [:*, visit(value), [:cos, value]] },
+ [:cos, any] => proc { |value| [:*, visit(value), [:-, [:sin, value]]] },
+ [:+, any, any] => proc { |left, right| [:+, visit(left), visit(right)] },
+ [:*, any, any] => proc do |left, right|
+ [:+, [:*, visit(left), right], [:*, left, visit(right)]]
+ end
+ }
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)
+ Evaluator.visit(@sexp, environment)
end
def simplify
- Expr.new(SimplificationVisitor.visit @sexp)
+ Expr.new(Simplifier.visit @sexp)
end
def derive(variable)
- Expr.new(DerivationVisitor.visit(@sexp, variable)).simplify
+ Expr.new(Derivator.visit(@sexp, variable)).simplify
end
end