(= a (ci 3))
(= b (ci 4))
(defobserver c ()
(prs "Hey, C is now" new-value
"was" old-value))
(= c (c? (sqrt (+ (expt (cval a) 2)
(expt (cval b) 2)))))
Hey, C is now 5 was unbound
(cset a 6 b 8)
Hey, C is now 10 was 5
In other words, values are not just functions of values but also active bits of state which can have arbitrary observer logic bound to them.
In a subsequent (next?) blog I plan to easy up on the tutorial angle and just present a decent port of what I called Cells II, which was powerful but had theoretical holes closed in Cells3.
A close version of what follows is in CVS here (as cells-1.arc in case this link does not fly):
http://common-lisp.net/cgi-bin/viewcvs.cgi/kennysarc2
/cells-1.arc?rev=1.1&root=cells&view=log
Enjoy.]
First a couple of utilities:
(def prt args
(apply prs args)
(prn))
(mac prun (banner . forms)
`(do (prn ,banner)
(prn '(do ,@forms))
(prn)
,@forms))
Let's start with the moral equivalent of a C++ member function, or at least I think that's what they call it. Something that looks like a stored data member or slot of an attribute of an object but is in fact implemented as a function of that object.
An example would be having a rectangle object with data members where one could store length and width and then have an area attribute implemented (in C++) as:
area = this.length * this.width;
Aside from saving a little memory, one gets a guarantee that the area will always be consistent with the length and width, which is not the case if one is writing code that says oh gosh I just changed the length I better go change the area.
As our 'application pushing down on the core' we'll use my favorite, a furnace boiler.
(= b* (obj outside-temp 72
on? [< _!outside-temp 50])))
No, the outside temp is not an attribute of a boiler, we're just keeping things in one table as a convenience until we get the ball rolling, later on we'll deal with multiple objects.
That anonymous function above boils down to:
If the outside temp is less than 50,
then turn on the boiler,
otherwise turn it off.
First, let's see if the rule works (not a big accomplishment)
(prt 'boiler b*!outside-temp (if (b*!on? b*) 'on 'off)))
-> boiler 72 off
Good, now change temp to 32 and see if the boiler comes on:
(= b*!outside-temp 32)
(prt 'boiler b*!outside-temp (if (b*!on? b*) 'on 'off)))
-> boiler 32 on
Super. Now let's hide the fact that on? is a function
behind a reader function:
(def on? (i) (i!on? i)))
...and ease inspection:
(def pr-boiler (b)
(prt 'boiler 'temp b*!outside-temp (if (on? b) 'on 'off)))
Test new slot reader, setting temp high enough this time
so that the boiler should go off:
(= b*!outside-temp 80)
(pr-boiler b*))
-> boiler temp 80 off
But we want more flexibility than having an attribute always defined by a function. Maybe we just want to store nil or t in
on? and maintain it as usual, via assignment. Now on? can no longer be assumed to be a function. Fortunately we already have it behind a reader in our burgeoning little OO system, so we just need to enhance that (and get a redefinition warning):
(def on? (i)
(awhen i!on?
(if (isa it 'fn)
(it i)
it)))
Just test that our new accessor for
on?:
(= b* (obj outside-temp -10
on? nil)) ;; we'll ignore the outside-temp for this test
(pr-boiler b*))
boiler temp -10 off
Now assign t to
on?:
(= b*!on? t) ; We'll hide the assignment implementation later.
(pr-boiler b*))
boiler temp -10 on
Good. We will want all our attributes to work this way, so we may as well generalize the
on? behavior now:
(def slot-value (i slot-name) ;; i is like self ala Smalltalk
(awhen i.slot-name
(if (isa it 'fn)
(it i)
it))))
(mac defslot (name)
`(def ,name (i) (slot-value i ',name)))
(defslot outside-temp)
(defslot on?)
(defslot inside-temp) ;; Let's start elaborating the model
(def pr-boiler (i)
(prt 'boiler
'outside-temp (outside-temp i)
(if (on? i) 'on 'off)
'inside-temp (inside-temp i)))
And test:
(= b* (obj outside-temp 20
on? nil
inside-temp [if (on? _)
72
_!outside-temp])))
boiler outside-temp 20 off inside-temp 20
Now let's bring back the automatic boiler:
(= b*!on? [< _!outside-temp 50]))
...and step the outside temperature up from freezing to torrid.
(loop (= b*!outside-temp 30)
(< b*!outside-temp 100)
(= b*!outside-temp (+ b*!outside-temp 10))
(pr-boiler b*)))
Looks like we need an air conditioner. And let's get more realistic about the model
(= outside* (obj temp 20))
(defslot temp)
(= furnace* (obj on? [< (temp outside*) 50]))
(= ac* (obj on? [> (temp outside*) 75])) ;; air conditioner
(= inside* [if (on? furnace*) 72
(on? ac*) 68
(temp outside*)])
(def dumpworld ()
(prt "outside" (temp outside*))
(prt "furnace" (if (on? furnace*) 'on 'off))
(prt "a/c" (if (on? ac*) 'on 'off))
(prt "inside" (temp inside*)))
Step temperature up from freezing to torrid, but with an air-conditioner:
(loop (= outside*!temp 30)
(< outside*!temp 100)
(= outside*!temp (+ outside*!temp 10))
(prn)
(dumpworld)))
Nice. We have built a working model that runs by itself given simple declarative rules, meaning we state the rules and an engine sees to it that the model runs. But we have a problem. Let's add a debug option to our slots:
(def slot-value (i slot-name (o debug))
(awhen i.slot-name
(if (isa it 'fn)
(do
(when debug (prt "Running the rule for slot" slot-name))
(let result (it i)
(when debug (prt "...slot" slot-name "is" result))
result))
it)))
(mac defslot (name (o debug))
`(def ,name (i) (slot-value i ',name ,debug)))
(defslot on? t)
Same test tracing the
on? slots
(loop (= outside*!temp 30)
(< outside*!temp 100)
(= outside*!temp (+ outside*!temp 10))
(prn)
(dumpworld)))
Looks OK, but watch what happens even if nothing is going on:
(dumpworld))
Ah, the downside of the functional paradigm: the code runs and runs. For simple functions that is no problem, but if we build an entire application this way things bog down (we learned the usual way).
What we need to cache the result of a calculation and then return the cached result when queried a second time. But then there is a new problem: when do we refresh the cache? Answer: when we have to in order to stay current with the changing world arounds us, more plainly when one of the values used in a calculation changes.
So we need to keep track of who uses whom in their calculations, and when one value changes notify its users that they need to recalculate.
Tomorrow.

1 comments:
情趣用品,情趣用品,飛機杯,自慰套,充氣娃娃,AV女優.按摩棒,跳蛋,潤滑液,角色扮演,情趣內衣
免費視訊聊天,辣妹視訊,視訊交友網,美女視訊,視訊交友,視訊交友90739,成人聊天室,視訊聊天室,視訊聊天,視訊聊天室,情色視訊,情人視訊網,視訊美女
一葉情貼圖片區,免費視訊聊天室,免費視訊,ut聊天室,聊天室,豆豆聊天室,尋夢園聊天室,聊天室尋夢園,影音視訊聊天室,
辣妹視訊,美女視訊,視訊交友網,視訊聊天室,視訊交友,視訊美女,免費視訊,免費視訊聊天,視訊交友90739,免費視訊聊天室,成人聊天室,視訊聊天,視訊交友aooyy
哈啦聊天室,辣妺視訊,A片,色情A片,視訊,080視訊聊天室,視訊美女34c,視訊情人高雄網,視訊交友高雄網,0204貼圖區,sex520免費影片,情色貼圖,視訊ukiss
A片下載,成人影片下載,免費A片下載,日本A片
影音視訊聊天室
Post a Comment