Hi all,
I am having a strange problem with anonymous blocks. I have been trying to impliment a Reccurrence rule engine. After trying different implimentations I didn't like, I found that the best solution involved an infinite list. So I set out to impliment lazy evaluation in smalltalk. What I have now works like: ((LazyElement enumerateFrom: 2006 with: [:e| e + 2]) collectAndFlatten: [:y| (LazyElement fromArray: #(1 8 12)) collectAndFlatten: [:m| (LazyElement fromArray: #(1 15 27)) collect: [:d| Date year: y month: m day: d ] ] ]) take: 500 And this part works perfectly. I have gone up to 2k dates and I noticed no delay at all (though I haven't timed it). I then changed the List creation parts to be methods, which is what my final solution will need. If I change the month function to return different months depending on the year and it works perfectly. But if I have a year for which no months apply and return an empty list, I get "error: Attempt to evaluate a block that is already evaluated." I have done a parralell implimentation in Ocaml (because it also requires me to manually build the Lazy List operations) and everything worked as expected, so I am really baffelled on this one. Nothing should be happening in the empty list case that doesn't already happen in the case of 1 or more in the list. I read on the list that the solution to this problem is to copy or clone the block. I have put a clone on every place I use a block and it didn't help. Nor did copy. I have enclosed the file out of the Lazy list classes (it's not a lot of code), I would appreciate any help anyone could give me. Thanks, Jason _________________________________________________________________ View Athletes Collections with Live Search http://sportmaps.live.com/index.html?source=hmemailtaglinenov06&FORM=MGAC01 LazyElement.st (5K) Download Attachment LazyNil.st (2K) Download Attachment LazyTest.st (1K) Download Attachment LazyValue.st (1K) Download Attachment |
From: "J J" <[hidden email]> wrote: > Hi all, > > I am having a strange problem with anonymous blocks. > > I read on the list that the solution to this problem is to copy or clone the > block. Yes, that is a good advice. > I have enclosed the file out of the Lazy list classes (it's not a lot of > code), I would appreciate any help anyone could give me. With that code it is not too difficult to understand what you are doing. The problem with our good old Squeak is that blocks cannot be used recursively. To see that problem. evaluate: | fac | fac := [:int | int = 1 ifTrue: [1] ifFalse: [int* (fac value: int - 1)] ]. fac value: 2 You will get exactly the error message that you got with your code. When you do this: | fac | fac := [:int | int = 1 ifTrue: [1] ifFalse: [int* (fac clone value: int - 1)] ]. fac clone value: 5 you will get the expected result. In your code, you should modify method LazyElement>> inject:into: inject: aValue into: aBinaryBlock "NOTE: This method does not behave as a normal inject into. It actually behaves exactly like fold_right" ^ aBinaryBlock clone " <- clone here " value: head value: (LazyValue delay: [ tail force inject: aValue into: aBinaryBlock]) That should help. A slightly different approach introduces an instance method that creates a block instance: flattenBlock ^[:p :e| p append: e ] and modifies methods flatten and inject:into: flatten ^ self inject: LazyNil new into: [self flattenBlock] inject: aValue into: aBinaryBlockGenerator "NOTE: This method does not behave as a normal inject into. It actually behaves exactly like fold_right" ^ aBinaryBlockGenerator value " this value calls a block that creates a new block instance " value: head value: (LazyValue delay: [ tail force inject: aValue into: aBinaryBlockGenerator]) > Thanks, > Jason I know that this is tricky stuff, but I also hope that my remarks are to some extent helpful. Greetings, Boris |
>From: "Boris Gaertner" <[hidden email]>
>Reply-To: The general-purpose Squeak developers >list<[hidden email]> >To: "The general-purpose Squeak developers >list"<[hidden email]> >Subject: Re: Confusing issue with blocks >Date: Tue, 12 Dec 2006 21:37:41 +0100 > >The problem with our good old Squeak is that blocks cannot be used >recursively. To see that problem. evaluate: > But this is what is confusing to me. The way I'm using it is recursively and it is working. Except in this one case. Let me try to illistrate what I mean. In the following [] means the nil list, which in my implimentation is LazyNil. In the call that works, the code does the following for (years = 2006 ..., months = 1,8, days = 1,15,28): 2006 :: 1 :: 1 2006 :: 1 :: 15 2006 :: 1 :: 28 2006 :: 1 :: [] "List terminated, unwinds one level and starts over on next month" 2006 :: 8 :: 1 2006 :: 8 :: 15 2006 :: 8 :: 28 2006 :: 8 :: [] 2006 :: [] "Month list terminated, unwinds back to year" 2007 :: 1 :: 1 etc. And this works. But in the case that the month list is empty for 2007 I get the error: 2006 :: 1 :: 1 "as before" 2006 :: 1 :: 15 2006 :: 1 :: 28 2006 :: 1 :: [] "List terminated, unwinds one level and starts over on next month" 2006 :: 8 :: 1 2006 :: 8 :: 15 2006 :: 8 :: 28 2006 :: 8 :: [] 2006 :: [] "Month list terminated, unwinds back to year" 2007 :: [] "No months for this year" 2008 :: 1 :: 1 "Should happen but does not" This is what I don't get. The two cases seem to me to be virtually identical. It is as if squeak needs a list of at least one element to get constructed so some internal counter can get reset or something. In Ocaml my implementation is almost word for word the same (obviously I use Ocaml idioms in ocaml but the behavior is exactly the same) and this part works. And in fact in the stack trace I see that Smalltalk does indeed calculate the next date (2008,1,1 in this example), it just throws the exception. Very strange. >In your code, you should modify method >LazyElement>> inject:into: > >inject: aValue into: aBinaryBlock > "NOTE: This method does not behave as a normal > inject into. It actually behaves exactly like fold_right" > > ^ aBinaryBlock clone " <- clone here " > value: head > value: (LazyValue delay: [ tail force inject: aValue into: >aBinaryBlock]) > >That should help. Actually I had the clone in the place you suggest and inside the delay. Every place you see a block used in that code has had a clone and a copy statement at some point, but nothing has fixed it so far. I took them out to make sure the meaning of the code was clear as well as make the bug easily reproducable. If you run the method "showWorking" it produces 500 dates without incident, despite being a completely recursive algorythm. If you run "showNotWorking" you get the failure behavior. >A slightly different approach introduces an instance method >that creates a block instance: > >flattenBlock > > ^[:p :e| p append: e ] > >and modifies methods flatten and inject:into: > >flatten > > ^ self inject: LazyNil new into: [self flattenBlock] > > >inject: aValue into: aBinaryBlockGenerator > "NOTE: This method does not behave as a normal inject into. It actually >behaves exactly like fold_right" > > ^ aBinaryBlockGenerator value " this value calls a block that creates > a new block instance " > value: head > value: (LazyValue delay: [ tail force inject: aValue into: >aBinaryBlockGenerator]) > > > Thanks, > > Jason > >I know that this is tricky stuff, but I also hope that my remarks >are to some extent helpful. > Of course. Thank you very much for your help and response. But as I have described this, I believe I figured out what the issue is: In my implementation a LazyList is either a LazyNil (meaning empty list) or a two element object that the first object is a real value and the second is basically a function that returns the next sequence recursively (i.e. the next sequence can again be a LazyNil or a function). The consequence of this is that you can have as much complicated nested lists, method calls etc. as you want but only 1 value will actually be calculated. But each time a value is asked for *1* must be returned. So in the case where each list has a value, the algorithm must only call each function once to get that value, all the recursive calls are delayed. But in the case that months returns nothing, the algorithm can't go into a delay, it has to continue searching until it comes up with something. This is creating the case where the same block actually ends up being used twice in the same call stack. So that leads to the question, why didn't cloning everything fix it? :( What would I need to do to get squeak to instantiate closures instead of blockContexts? I wish that one of the following 3 things would happen: - Squeak would switch completely to proper closures - Squeak would, during compile phase, determine if the block should be a closure or block context (e.g. does the block contain ^?) - Squeak would provide some simple way to say "please instantiate these specific blocks as closures" (maybe this one exists?) Thanks, Jason _________________________________________________________________ View Athletes Collections with Live Search http://sportmaps.live.com/index.html?source=hmemailtaglinenov06&FORM=MGAC01 |
> - Squeak would provide some simple way to say "please instantiate these
> specific blocks as closures" (maybe this one exists?) Sure, use an image with the new compiler (such as http://www.iam.unibe.ch/~denker/temp/NewCompiler.zip). Then either change the global settings or implement a method like LazyWhatsoever class>>compilerClass ^ ClosureCompiler in your classes where you want closures. Lukas -- Lukas Renggli http://www.lukas-renggli.ch |
Thanks Lukas. I will keep that in mind. It turns out that one call to
clone fixed it. I changed: ^ aBinaryBlock value: head value: (LazyValue delay: [ tail force inject: aValue into: aBinaryBlock ]) to: ^ aBinaryBlock clone value: head value: (LazyValue delay: [ tail force inject: aValue into: aBinaryBlock ]) I had already tried doing: cloneBlock := aBinaryBlock clone. ^ cloneBlock value: head value: (LazyValue delay: [ tail force inject: aValue into: cloneBlock ]) But it didn't work. But anyway, it's working now. Thanks for the help all. >From: "Lukas Renggli" <[hidden email]> >Reply-To: The general-purpose Squeak developers >list<[hidden email]> >To: "The general-purpose Squeak developers >list"<[hidden email]> >Subject: Re: Re: Confusing issue with blocks >Date: Wed, 13 Dec 2006 08:45:15 +0100 > >>- Squeak would provide some simple way to say "please instantiate these >>specific blocks as closures" (maybe this one exists?) > >Sure, use an image with the new compiler (such as >http://www.iam.unibe.ch/~denker/temp/NewCompiler.zip). Then either >change the global settings or implement a method like > >LazyWhatsoever class>>compilerClass > ^ ClosureCompiler > >in your classes where you want closures. > >Lukas > >-- >Lukas Renggli >http://www.lukas-renggli.ch > _________________________________________________________________ WIN up to $10,000 in cash or prizes enter the Microsoft Office Live Sweepstakes http://clk.atdmt.com/MRT/go/aub0050001581mrt/direct/01/ |
Free forum by Nabble | Edit this page |