Skip to content

Instantly share code, notes, and snippets.

@nileshtrivedi
Created October 11, 2011 15:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nileshtrivedi/1278438 to your computer and use it in GitHub Desktop.
Save nileshtrivedi/1278438 to your computer and use it in GitHub Desktop.
Simple symbolic math implementation in Ruby (Work in progress)
# TODO
# if a var has a value, it also needs a name. This should not be necessary. Variable.initialize should accept hashargs
# Expression.to_s should output in infix-notation (like normal humans)
# add more syntactic sugar by manipulating Ruby Numeric classes. Add symbolic operators to Ruby's Numeric
# If symbolic expression contains variables without value then it should return nil (should it, really?)
# Implement additional operators like **
# All symbolic expression should be automatically simplified when created:
# cons(0) * x # => 0
# 2 + x + 1 # => x + 3
# -(x-y) + 2*x # => x + y
# (x**2)**3 / x # => x**5
# this is purely for syntactic-sugar
class Object
private
def var(name = '_', value = nil)
Variable.new(name, value)
end
def cons(value) # value is a literal constant as supported by ruby
Constant.new(value)
end
end
class Numeric
def is_zero?
0 == self
end
def is_one?
1 == self
end
end
# this module gets included in Variable, Constant and Expression
module Operators
def +(op)
return self if op.is_zero?
return op if self.is_zero?
return Constant.new(self.value.+(op.value)) if self.is_a?(Constant) && op.is_a?(Constant)
Expression.new(:+, self, op)
end
def -(op)
return self if op.is_zero?
return Constant.new(self.value.-(op.value)) if self.is_a?(Constant) && op.is_a?(Constant)
Expression.new(:-, self, op)
end
def *(op)
return Constant.new(0) if op.is_zero? || self.is_zero?
return self if op.is_one?
return op if self.is_one?
return Constant.new(self.value.*(op.value)) if self.is_a?(Constant) && op.is_a?(Constant)
Expression.new(:*, self, op)
end
def /(op)
return self if op.is_one?
return Constant.new(self.value./(op.value)) if self.is_a?(Constant) && op.is_a?(Constant)
Expression.new(:/, self, op)
end
end
class Variable
include Operators
attr_accessor :name, :value
def initialize(name = '_', value = nil) # nil stands for unknown value
@name = name
@value = cons(value) if value
end
def value=(val)
@value = (val.is_a?(Constant) ? val : cons(val))
end
def to_s
@name.to_s
end
def is_zero?
false
end
def is_one?
false
end
def value_or_self
@value || self
end
def variables
[self]
end
end
class Constant
include Operators
attr_accessor :value # these will be ruby primitive objects like Fixnum, Float etc
def initialize(value)
raise "constant must have a numeric value, not #{value.inspect}." if value.nil? || !value.is_a?(Numeric)
@value = value
end
def is_zero?
@value == 0
end
def is_one?
@value == 1
end
def to_s
@value.to_s
end
def value_or_self
self # cannot do @value || self here because @value is a ruby-primitive which we are not modifying to avoid monkeypatching std ruby classes
end
def variables
[]
end
end
class Expression # one operator and two operands in a tree-like structure
include Operators
attr_accessor :operator, :op_left, :op_right
def initialize(operator, op_left, op_right)
@operator = operator; @op_left = op_left; @op_right = op_right # op_left and op_right are either expressions or constants or variables.
end
def to_s
"(#{@operator} #{@op_left} #{@op_right})" # lisp-style prefix-notation so that we don't have to worry about operator precedence
end
def value # returns as simplified expression as possible
@op_left.value_or_self.send(@operator, @op_right.value_or_self)
end
def value_or_self
value
end
def is_zero?
false
end
def is_one?
false
end
def variables
(@op_left.variables + @op_right.variables).uniq
end
def operations
# TODO
end
end
x = var
x.value = 3
f = cons(2)*x + cons(1)
puts f
puts f.value
z = var
puts (z + cons(1)).value # TODO should expression.value return nil if it containst unset variables? Right now, it returns
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment