A
Alexander Schmolck
[trimmed groups]
Count me in.
do you mean 30?
I've translated my interpretation of the above to this actual python code:
from math import sin, pi
v1 = cell(lambda: 1)
a = cell(lambda:-(sin(pi/2)+v1.val), dependsOn=[v1])
b = cell(lambda: -a.val*10, dependsOn=[a],
onChange=lambda *args: printChangeBlurp(name='b',*args))
print 'v1 is', v1
print 'a is', a # -2.0 ;; this and the next are easy
print 'b is', b # 20
v1.val = 2 # ;; fun part starts here
print 'v1 now is', v1
print 'b now is', b # 30 ;; of course a got updated, too
I get the following printout:
v1 is 1
a is -2.0
b is [cell 'b' changed from <__main__.unbound object at 0xb4e2472c> to 20.0,
it was not bound]20.0
[cell 'b' changed from 20.0 to 30.0, it was bound ] v1 now is 2
b now is 30.0
Does that seem vaguely right?
Is the above what you want (you can also dynamically assign onChange later
on, as required or have a list of procedures instead)?
Updating on write rather than recalculating on read does in itself not seem
particularly complicated.
OK, so in what way does the quick 35 line hack below also completely miss your
point?
# (NB. for lispers: 'is' == EQ; '==' is sort of like EQUAL)
def printChangeBlurp(someCell, oldVal, newVal, bound, name=''):
print '[cell %r changed from %r to %r, it was %s]' % (
name, oldVal, newVal, ['not bound', 'bound '][bound]),
_unbound = type('unbound', (), {})() # just an unique dummy value
def updateDependents(dependents):
seen = {}
for dependent in dependents:
if dependent not in seen:
seen[dependent] = True
dependent.recalculate()
updateDependents(dependent._dependents)
class cell(object):
def __init__(self, formula, dependsOn=(), onChange=None):
self.formula = formula
self.dependencies = dependsOn
self.onChange = onChange
self._val = _unbound
for dependency in self.dependencies:
if self not in dependency._dependents:
dependency._dependents.append(self)
self._dependents = []
def __str__(self):
return str(self.val)
def recalculate(self):
newVal = self.formula()
if self.onChange is not None:
oldVal = self._val
self.onChange(self, oldVal, newVal, oldVal is not _unbound)
self._val = newVal
def getVal(self):
if self._val is _unbound:
self.recalculate()
return self._val
def setVal(self, value):
self._val = value
updateDependents(self._dependents)
val = property(getVal, setVal)
'as
Ken Tilton said:yes, but do not feel bad, everyone gets confused by the /analogy/ to
spreadsheets into thinking Cells /is/ a spreadsheet. In fact, for a brief
period I swore off the analogy because it was so invariably misunderstood.
Even Graham misunderstood it.
Count me in.
Very roughly speaking, that is supposed to be the code, not the output. So you
would start with (just guessing at the Python, it has been years since I did
half a port to Python):
v1 = one
a = determined_by(negate(sin(pi/2)+v1)
b = determined_by(negate(a)*10)
print(a) -> -2.0 ;; this and the next are easy
print(b) -> 20
v1 = two ;; fun part starts here
print(b) -> 40 ;; of course a got updated, too
do you mean 30?
I've translated my interpretation of the above to this actual python code:
from math import sin, pi
v1 = cell(lambda: 1)
a = cell(lambda:-(sin(pi/2)+v1.val), dependsOn=[v1])
b = cell(lambda: -a.val*10, dependsOn=[a],
onChange=lambda *args: printChangeBlurp(name='b',*args))
print 'v1 is', v1
print 'a is', a # -2.0 ;; this and the next are easy
print 'b is', b # 20
v1.val = 2 # ;; fun part starts here
print 'v1 now is', v1
print 'b now is', b # 30 ;; of course a got updated, too
I get the following printout:
v1 is 1
a is -2.0
b is [cell 'b' changed from <__main__.unbound object at 0xb4e2472c> to 20.0,
it was not bound]20.0
[cell 'b' changed from 20.0 to 30.0, it was bound ] v1 now is 2
b now is 30.0
Does that seem vaguely right?
The other thing we want is (really inventing syntax here):
on_change(a,new,old,old-bound?) print(list(new, old, old-bound?)
Is the above what you want (you can also dynamically assign onChange later
on, as required or have a list of procedures instead)?
Then the print statements Just Happen. ie, It is not as if we are just hiding
computed variables behind syntax and computations get kicked off when a value
is read. Instead, an underlying engine propagates any assignment throughout
the dependency graph before the assignment returns.
Updating on write rather than recalculating on read does in itself not seem
particularly complicated.
My Cells hack does the above, not with global variables, but with slots (data
members?) of instances in the CL object system. I have thought about doing it
with global variables such as a and b above, but never really seen much of
need, maybe because I like OO and can always think of a class to create of
which the value should be just one attribute.
OK, so in what way does the quick 35 line hack below also completely miss your
point?
# (NB. for lispers: 'is' == EQ; '==' is sort of like EQUAL)
def printChangeBlurp(someCell, oldVal, newVal, bound, name=''):
print '[cell %r changed from %r to %r, it was %s]' % (
name, oldVal, newVal, ['not bound', 'bound '][bound]),
_unbound = type('unbound', (), {})() # just an unique dummy value
def updateDependents(dependents):
seen = {}
for dependent in dependents:
if dependent not in seen:
seen[dependent] = True
dependent.recalculate()
updateDependents(dependent._dependents)
class cell(object):
def __init__(self, formula, dependsOn=(), onChange=None):
self.formula = formula
self.dependencies = dependsOn
self.onChange = onChange
self._val = _unbound
for dependency in self.dependencies:
if self not in dependency._dependents:
dependency._dependents.append(self)
self._dependents = []
def __str__(self):
return str(self.val)
def recalculate(self):
newVal = self.formula()
if self.onChange is not None:
oldVal = self._val
self.onChange(self, oldVal, newVal, oldVal is not _unbound)
self._val = newVal
def getVal(self):
if self._val is _unbound:
self.recalculate()
return self._val
def setVal(self, value):
self._val = value
updateDependents(self._dependents)
val = property(getVal, setVal)
'as