A small go library to lazily calculate dependent variables.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

104 lines
1.8 KiB

package lazy
import (
"github.com/pkg/errors"
"gitlab.com/Pixdigit/uniqueID"
)
var ErrCircularDependency = errors.New("circular dependency while resolving")
var VarIDSpace = uniqueID.IDSpace{}
type Var struct {
id uniqueID.ID
isResolving bool
IsResolved bool
Value float64
evaluator func() float64
dependencies []Var
}
func Invariant(value float64) Var {
v := NewVar(func() float64 {
return value
})
v.IsResolved = true
return v
}
func NewVar(eval func() float64) Var {
return Var{
VarIDSpace.NewID(),
false,
false,
0,
eval,
make([]Var, 0),
}
}
func PointerVar(value *float64) Var {
return NewVar(func() float64 {
return *value
})
}
func Combine(v1, v2 Var, combinator func(float64, float64) float64) Var {
v := NewVar(func() float64 {
return combinator(v1.evaluator(), v2.evaluator())
})
v.dependencies = []Var{v1, v2}
return v
}
func Add(v1, v2 Var) Var {
return Combine(v1, v2, func(arg1 float64, arg2 float64) float64 {
return arg1 + arg2
})
}
func Sub(v1, v2 Var) Var {
return Combine(v1, v2, func(arg1 float64, arg2 float64) float64 {
return arg1 - arg2
})
}
func Mul(v1, v2 Var) Var {
return Combine(v1, v2, func(arg1 float64, arg2 float64) float64 {
return arg1 * arg2
})
}
func Div(v1, v2 Var) Var {
return Combine(v1, v2, func(arg1 float64, arg2 float64) float64 {
return arg1 / arg2
})
}
func (v *Var) HasDependency(v2 Var) bool {
for _, dep := range v.dependencies {
if dep.id == v2.id {
return true
}
if dep.HasDependency(v2) {
return true
}
}
return false
}
func (v *Var) Resolve() error {
v.isResolving = true
defer func() { v.isResolving = false }()
for _, dep := range v.dependencies {
if dep.isResolving {
return ErrCircularDependency
}
if !dep.IsResolved {
dep.Resolve()
}
}
v.Value = v.evaluator()
v.IsResolved = true
return nil
}