In a Sequencable collection, doWithIndex seems to just call withIndexDo. Is there a "do" that passes just the index, not the object?
My son is working on a roguelike, and we're looking at various ways of creating the map. This is a two-dimensional map that would fit in a character-mode, and we have this so far:
| map xBound yBound | xBound := 80. yBound := 24. map := Array new: xBound. i := 1. [i <= xBound] whileTrue: [ map at:i put: (Array new:yBound). i := i + 1 ].
map do: [ :col| col withIndexDo: [:obj :y| col at:y put:#stone. ] ].
This seems to lack elegance. Better would be:
map := 2DArray new: 80@25. map doXY: [:xy| map atXY:xy put:#stone].
Or even:
grid := Grid new: 80@25 fill: #stone.
Marcus has his 2DArray but it's peppered with caveats.
Thoughts?
===Blake===
If you just want the index, you would do something like: 1 to: collection size do: [:i | "stuff goes here" ].
If you want to fill an entire collection with a single value, try collection atAllPut: #rock.
You can use one collection of numbers to index the other.
aCollection atAll: #(1 3 7 9 12 ) put: #rock.
You might look at Interval as well. It pretends to be a collection and you can iterate over it.
(Interval from: 1 to: 18 by: 3) do: [:ea | Transcript show: ea asString; space] produces the sequence 1 4 7 10 13 16
I would probably do the grid class idea.
Object subclass: #Grid instanceVariables: 'rows'.....
initializeWithPoint: aPoint
rows := (interval 1 to: aPoint x) collect: [:r | Array new: aPoint y].
atAllPut: anObject
rows do: [:r | r atAllPut: anObject]
atRow: row column: col
^(rows at: row) at: col
atRow: row column: col put: anObject
^(rows at: row) at: col put: anObject
rowsDo: aBlock
rows do: aBlock
cellsDo: aBlock
self rowsDo: [:row | row do: aBlock]
etc....
On Feb 3, 2007, at 5:44 PM, Blake wrote:
In a Sequencable collection, doWithIndex seems to just call withIndexDo. Is there a "do" that passes just the index, not the object?
My son is working on a roguelike, and we're looking at various ways of creating the map. This is a two-dimensional map that would fit in a character-mode, and we have this so far:
| map xBound yBound | xBound := 80. yBound := 24. map := Array new: xBound. i := 1. [i <= xBound] whileTrue: [ map at:i put: (Array new:yBound). i := i + 1 ].
map do: [ :col| col withIndexDo: [:obj :y| col at:y put:#stone. ] ].
This seems to lack elegance. Better would be:
map := 2DArray new: 80@25. map doXY: [:xy| map atXY:xy put:#stone].
Or even:
grid := Grid new: 80@25 fill: #stone.
Marcus has his 2DArray but it's peppered with caveats.
Thoughts?
===Blake=== _______________________________________________ Beginners mailing list Beginners@lists.squeakfoundation.org http://lists.squeakfoundation.org/mailman/listinfo/beginners
On Sat, 03 Feb 2007 20:44:50 -0800, Todd Blanchard tblanchard@mac.com wrote:
If you just want the index, you would do something like: 1 to: collection size do: [:i | "stuff goes here" ].
You know, I didn't consider that because it's...extrinsic.
If you want to fill an entire collection with a single value, try collection atAllPut: #rock.
Right. Thanks.
I would probably do the grid class idea.
Object subclass: #Grid instanceVariables: 'rows'.....
But not descend from Array?
On Feb 4, 2007, at 12:35 AM, Blake wrote:
But not descend from Array?
I would not as you run the risk of mixing concepts. Arrays are 1D so new: expects an integer. You want to make a new: that takes a point and have an entire interface written in terms of 2D. It can get messy if you're trying to keep track of which method is 1D vs 2D oriented. IMO, best to keep your interface minimal and specific, implementing it with an internal array.
In general, inheritance is overused as a composition mechanism. It should be the last resort, not the first.
-Todd Blanchard
On Sat, 03 Feb 2007 20:44:50 -0800, Todd Blanchard tblanchard@mac.com wrote:
initializeWithPoint: aPoint
rows := (interval 1 to: aPoint x) collect: [:r | Array new: aPoint y].
Also, stylistically, is it preferred to have an instance-based initializer rather than a class-based variation on "new"?
Hi Blake,
on Sun, 04 Feb 2007 10:35:33 +0100, you wrote:
On Sat, 03 Feb 2007 20:44:50 -0800, Todd Blanchard wrote:
initializeWithPoint: aPoint
rows := (interval 1 to: aPoint x) collect: [:r | Array new: aPoint y].
Also, stylistically, is it preferred to have an instance-based initializer rather than a class-based variation on "new"?
You might want to compare:
class side>>new | instance | instance := self new. instance thisAndThat "extra code needed". ^ instance
instance side>>initialize "sent automatically by Behavior>>new" iVar1 := 'text'. iVar2 := 0
The latter is preferable over the former (less code, less maintenance). But often people put utility methods like #on:, #with: etc on the class side, for non-trivial initializations.
From the (re-)usability point of view, if you had getters/setters (like in traits), the perfect approach is x := MyClass new setY: 'text'; setZ: 0; yourself.
Or, like I prefer to do it (x := MyClass new) setY: 'text'; setZ: 0.
/Klaus
On Sun, 04 Feb 2007 02:16:09 -0800, Klaus D. Witzel klaus.witzel@cobss.com wrote:
You might want to compare:
class side>>new | instance | instance := self new. instance thisAndThat "extra code needed". ^ instance
instance side>>initialize "sent automatically by Behavior>>new" iVar1 := 'text'. iVar2 := 0
The latter is preferable over the former (less code, less maintenance). But often people put utility methods like #on:, #with: etc on the class side, for non-trivial initializations.
When you say "sent automatically by Behavior>>new" is that true? In other words:
x := abitraryObject new.
results in an attempt to call arbitraryObject>>initialize?
From the (re-)usability point of view, if you had getters/setters (like in traits), the perfect approach is x := MyClass new setY: 'text'; setZ: 0; yourself.
Or, like I prefer to do it (x := MyClass new) setY: 'text'; setZ: 0.
I suppose we don't worry much about the "waste" of, say, setting up some features with default values and then having to discard those when the user sets them to something actually useful?
===Blake===
Hi Blake,
on Fri, 09 Feb 2007 02:17:28 +0100, you wrote:
On Sun, 04 Feb 2007 02:16:09 -0800, Klaus D. Witzel wrote:
You might want to compare:
class side>>new | instance | instance := self new. instance thisAndThat "extra code needed". ^ instance
instance side>>initialize "sent automatically by Behavior>>new" iVar1 := 'text'. iVar2 := 0
The latter is preferable over the former (less code, less maintenance). But often people put utility methods like #on:, #with: etc on the class side, for non-trivial initializations.
When you say "sent automatically by Behavior>>new" is that true? In other words:
x := abitraryObject new.
results in an attempt to call arbitraryObject>>initialize?
Sure. Evaluate "Object halt; new" with doIt and then in the debugger, in the DoIt method line push the buttons "Through" then "Into".
From the (re-)usability point of view, if you had getters/setters (like in traits), the perfect approach is x := MyClass new setY: 'text'; setZ: 0; yourself.
Or, like I prefer to do it (x := MyClass new) setY: 'text'; setZ: 0.
I suppose we don't worry much about the "waste" of, say, setting up some features with default values and then having to discard those when the user sets them to something actually useful?
But yes we care :) The default initializer is Object>>#initialize, an empty method with is executed at primitive speed (it just returns self). Before that, #basicNew (the primitive) has already initialized the new instance with all nil's (or zeros, depending on the class' format spec). From the ~ 2887 classes in my Squeak-dev image, only some 928 implement initialize (even less when subtracting all the class side implementors).
So yes, it's quite common behavior to let the user set something actually useful.
/Klaus
===Blake===
On Thu, 08 Feb 2007 22:58:32 -0800, Klaus D. Witzel klaus.witzel@cobss.com wrote:
Sure. Evaluate "Object halt; new" with doIt and then in the debugger, in the DoIt method line push the buttons "Through" then "Into".
Aha.
Huh.
Why doesn't this:
^ self basicNew initialize
cause a DNU? Object doesn't have an "initialize" method.
But yes we care :) The default initializer is Object>>#initialize, an empty method with is executed at primitive speed (it just returns self). Before that, #basicNew (the primitive) has already initialized the new instance with all nil's (or zeros, depending on the class' format spec). From the ~ 2887 classes in my Squeak-dev image, only some 928 implement initialize (even less when subtracting all the class side implementors).
So yes, it's quite common behavior to let the user set something actually useful.
I wasn't clear. I'm accustomed to creating "reasonable defaults" for instances. In a lot of cases, this has struck me as dumb, since there are no reasonable defaults. I thought I saw the same thing going on in Squeak but that doesn't seem to be the case.
Hi Blake,
on Fri, 09 Feb 2007 09:15:43 +0100, you wrote:
On Thu, 08 Feb 2007 22:58:32 -0800, Klaus D. Witzel wrote:
Sure. Evaluate "Object halt; new" with doIt and then in the debugger, in the DoIt method line push the buttons "Through" then "Into".
Aha.
Huh.
Why doesn't this:
^ self basicNew initialize
cause a DNU? Object doesn't have an "initialize" method.
Really ? :) It is inherited from ProtoObject ;-)
/Klaus
On Fri, 09 Feb 2007 02:54:14 -0800, Klaus D. Witzel klaus.witzel@cobss.com wrote:
Why doesn't this:
^ self basicNew initialize
cause a DNU? Object doesn't have an "initialize" method.
Really ? :) It is inherited from ProtoObject ;-)
Wow, don't know how I missed that.
I guess maybe partly because the debugger doesn't go into the method (because it's blank, I suppose).
Thanks, Klaus.
Blake wrote:
On Fri, 09 Feb 2007 02:54:14 -0800, Klaus D. Witzel klaus.witzel@cobss.com wrote:
Why doesn't this:
^ self basicNew initialize
cause a DNU? Object doesn't have an "initialize" method.
Really ? :) It is inherited from ProtoObject ;-)
Wow, don't know how I missed that.
I guess maybe partly because the debugger doesn't go into the method (because it's blank, I suppose).
Actually, the method isn't blank, it's actually "^ self", except that that part is added automatically and you don't see it.
See here: http://users.ipa.net/~dwighth/smalltalk/bluebook/bluebook_chapter27.html#Com...
One of the optimisations in Smalltalk is that a method that's just "^ self" actually doesn't have any bytecodes but just a special flag in the method header. Glossing over a few details, the Interpreter picks this up and skips over it.
It's interesting to play with the right-most button on the Browser - click it and see the bytecodes or the "decompilation" of source.
Michael.
beginners@lists.squeakfoundation.org