I was going through (using Squeak) the Discovering Better Code: Bowling for Smalltalk Series by Ron Jeffries and I can't get pass through the third article.
A new class (called Frame) is being created which takes an array as an argument in the constructor.
Frame class>>new: anArray
^self new setRolls: anArray
Frame>>setRolls: anArray
rolls := anArray
When I try to run this in a simple test:
testFrame
| frame rolls |
rolls := Array with: 5 with: 4.
frame := Frame new: rolls.
I get the following error:
alt text http://files.getdropbox.com/u/120566/junk/error.png
How should I modify the #new message to be able to initialize Frame object with an array?
I guess you failed adding the method new: correctly to Frame class. Are you sure you put it on the class side (Frame class) and not on the instance side (Frame)? To do it, click on the 'class' button, before adding your method new:.
You really don't want to override new: here. new: is traditionally reserved for "Create an item of this integer size", and it doesn't surprise me that it's blowing up on you.
A more traditional name for the kind of constructor you want is fromArray:, or perhaps even fromCollection: which would probably have worked as you wished.
Related
I'm trying to create a live-coding environment in Smalltalk. The objects I'm using have long complex methods with many parameters for creating new instances. I want to be able to write a very short "command" that just specifies the parameters I'm interested in and provides default values for all the other parameters.
For instance instead of writing something like:
Polyhedron shape: #cube size: 1 rotationSpeed: 5 rotationDirection: 0.707 color: red texture: false
In my live coding environment I want to write:
poly shape: #cube
Some of the arguments could be complex and contain their own messages like:
poly shape: #cube rotationSpeed: ((pot1 max: 0) min: speedLimit)
So rather than just trying to parse the live coding string myself I thought it would be easier if I could use Smalltalk's built in parser or compiler to decode the string and call Polyhedron with the full creation message including default values.
How would I do that?
I've got as far as getting an array of tokens out of the parser but that doesn't identify message parameters and arguments. Before I tried to implement argument parsing myself I figured there's likely to be something in Smalltalk that could give me a list of message parameters and arguments?
It doesn't look like you have to tweak the compiler. To provide default values simply have your objects be born with them. How? In the initialize method:
Polyhedron >> initialize
super initialize.
shape := #triangle.
size := 1.
rotationSpeed := 5.
rotationDirection := 0.707.
color: Color red texture: false
The #initialize message is sent when you send Polyhedron new. Thus, if you want to change the shape, you only need to send
Polyhedron new shape: #cube
Note by the way that it is usually a good idea to send super initialize so to give superclasses a chance to perform their initialization routines.
Note also that Polyhedra is the plural of Polyhedron, and class names are usually in singular.
Addendum
In case you cannot edit the class Polyhedron, you can still reference it and therefore send messages to it. In particular, you can create new instance creation methods, shorter than the ones you already have. Here is how to do this:
Polyedron compile: 'shape: aSymbol
^self
shape: aSymbol
size: 1
rotationSpeed: 5
rotationDirection: 0.707
color: Color rd texture: false'
Note that the argument of #compile: is the source code of the method you want to add. By using this new method you will be able to just say
Polyedron shape: #cube
to get what you wanted.
I am interested in creating my own Stream subclass and I'm wondering what methods I should override (deploying on pharo and Gemstone). I have a collection with various types of things in it and I want to be able to stream over a subset of it, containing elements of a class. I don't want to copy the collection or use a collect: block because the collection may be large. My first use case is something like this:
stream := self mailBox streamOf: QTurnMessage.
stream size > 1
ifTrue: [ ^ stream at: 2 ]
ifFalse: [ ^ nil ]
Any pointers on what methods to override?
In Smalltalk, when we say Stream we refer to objects that respond to a basic protocol given by some few methods such as #next, #nextPut:, #contents, etc. So, before going into further detail I would say that stream at: 2, as you put in your example, is not a very appropriate expression. More appropriate expressions for a Stream would be
stream position: 2.
^stream next
So, the first thing you have to consider is whether you are looking for a Stream or a Collection. This basic decision depends on the behavior your objects will have to implement.
Use a subclass of Stream in case you decide that you want to enumerate the elements using #next, i.e. mostly in sequential order. However, if you want to access your elements via at: index, model your objects with a subclass of SequenceableCollection.
In case you choose streams, you will have to decide whether you will be only accessing them for reading operations or will also want to modify their contents. Your description of the problem seems to indicate that you will just read them. Therefore the basic protocol you have to implement first is
#next "retrieve the object at the following position and advance the position"
#atEnd "answer with true if there are no more objects left"
#position "answer the current position of the implicit index"
#position: "change the implicit index to a new value"
Also, if your streams will be read only, make your class a subclass of ReadStream.
There are some few other additional messages you will have to implement if you want to inherit fancier methods. An example would be #next: which retrives a subcollection of several consecutive elements (its size being given by the argument.)
If you instead think that it would be better to model your objects as collections, then the basic protocol you will have to implement consists of the following three methods
#at: index "retrieve the element at the given index"
#size "retrieve the total number of elements"
#do: aBlock "evaluate aBlock for every element of the receiver"
(I don't think your collections have to support at:put:.)
Very recently we had the same problem you are describing and decided to model our objects as collections (rather than streams.) However, regardless of which approach you will finally follow I think that you should try both and see which one is better. Nobody will give you better advice than your Smalltalk system.
Incidentally, note also that if you have a (sequenceable) Collection, you will get a Stream for free: just send #readStream to your collection!
I needed to override next and atEnd. My Stream subclass takes a block and a collection, and iterates over all elements of the collection for which block evaluates to true.
Example usage:
e := Array with: 1 with: 2 with: 3.
a := QStream collection: e block: [ :i| i odd ].
a next. "1"
a next. "3"
Here is the core of it:
Stream subclass: #QStream
instanceVariableNames: 'collection block index found'
classVariableNames: ''
poolDictionaries: ''
category: 'QDialog-Core'
initialize
super initialize.
index := 1.
self search.
next
| result |
result := found.
self search.
^result.
search
[ index < collection size and: [ (block value: (collection at: index)) not ] ]
whileTrue: [ index := index + 1 ].
self atEnd
ifTrue: [ ^ found := nil ]
ifFalse: [
found := collection at: index.
index := index + 1 ]
atEnd
^ index > collection size
I basically want to implement new which can accept argument e.x obj := SomeClass new: 'a'. I tried to implement this way
initialize: bdata
data := bdata
But this doesn't work. seems like I am doing some silly mistake because of lack of knowledge. I try to google it but couldn't find any example. Please help.
In Smalltalk, new and new: are not keywords, but regular messages. They are simply implemented by the object's class. To write a method for an objects's class (rather than for an instance), click the "class" button in the system browser. There, you could implement your new: method.
Note, however, that it is usually not a good idea to name your own instance creation method new:. Since this is a regular method, you can name it anything you want. For example, MyClass withBData: foo. Make it a nice descriptive name. It could look like
withBData: bdata
| inst |
inst := self new.
inst bdata: bdata.
^inst
Your code is too short to tell what is wrong. In general you should have an initialize with arg, something like this:
initialize: arg
self initialize.
instVar := arg
Then you can implement new: like this:
new: arg
^ self basicNew
initialize: arg;
yourself
Note that new is implemented as self basicNew initialize, so if you are calling initialize from your custom initialization method, you shouldn't use new in your custom new, use basicNew instead
You can use the basicNew method if you need to use your argument in the initialize method (as Uko mentions in his answer above).
withBData: bdata
^ (self basicNew bdata: bdata) initialize
I wrote the following method in smalltalk:
initializeWithStart: startWith step: theStep count: theCount
self initialize.
startNumber := startWith.
stepSize := theStep.
countUntil := theCount.
and i just want to call this method after creating an object from the workspace. so I wrote:
mySq := ArithmeticsS new.
mySq initializeWithStart: '2' step:'4' count:'10'.
why do I get error "MessageNotUnderstood:undefinedobject>>initializeWithStart:step:count:"?
We can't say for sure without more context, but it looks like you created the new method on the class side, instead of the instance side.
In the workspace, you chose to send the message to an instance.
To find out, evaluate (print)
ArithmeticsS respondsTo: #initializeWithStart:step:count:
If the method is on the class side, that will be true. Delete that method and save it on the instance side instead.
Now evaluate
ArithmeticsS new respondsTo: #initializeWithStart:step:count:
With new, this checks for your method on the instance side. It should be true. My guess from the information you posted is that it is false, which means that you didn't save the method in the right place.
Check the documentation for your dialect of Smalltalk to confirm how to save an instance method.
You should instantiate like this:
mySq := ArithmeticsS new initializeWithStart: 2 step:4 count:10.
I have an array of objects in MATLAB and I've called their constructors in a loop:
antsNumber = 5;
for counter = 1: antsNumber
ant(counter) = TAnt(source, target);
end
MATLAB warns me to use preallocation to speed up the process. I do know the benefits of preallocation but I don't know how to do that for objects.
Here are a few options, which require that you design the class constructor for TAnt so that it is able to handle a no input argument case:
You can create a default TAnt object (by calling the constructor with no input arguments) and replicate it with REPMAT to initialize your array before entering your for loop:
ant = repmat(TAnt(),1,5); %# Replicate the default object
Then, you can loop over the array, overwriting each default object with a new one.
If your TAnt objects are all being initialized with the same data, and they are not derived from the handle class, you can create 1 object and use REPMAT to copy it:
ant = repmat(TAnt(source,target),1,5); %# Replicate the object
This will allow you to avoid looping altogether.
If TAnt is derived from the handle class, the first option above should work fine but the second option wouldn't because it would give you 5 copies of the handle for the same object as opposed to 5 handles for distinct objects.
The following link might be of help:
http://www.mathworks.com/help/techdoc/matlab_oop/brd4btr.html#brd4nrh
Web archive of dead link
New link:
http://de.mathworks.com/help/matlab/matlab_oop/creating-object-arrays.html
The warning it gives is superfluous, unless you are doing computational heavy stuff, I would ignore it.
The reason why it's giving you the error, is because it has to find new space. Say, I give you a list of seven objects, and I tell you that you need to place them all in a row, I then go off, and give you a few more things you need to put somewhere. I then give you an eighth object and tell you to put it right after the seventh. Because you have stuff where the eighth object is, you either have to move it out of the way, or you have to move all seven objects. Matlab, is telling you it would be faster if you were to tell it beforehand that you want to put 5 things in there, rather than just giving it things one by one, having to look for a new spot each time. You can do that by adding this line to the top of your code:
ant = [1:5];
There are also other ways to do this too.
Not sure if I got your problem right, but if you want to initialize an array of your self-defined class "TAnt", here is how I would do it
For TAnt's constructor method, put something like:
function obj = TAnt(source, target)
if nargin > 0
obj.mySource = source;
obj.myTarget = target;
else
obj.mySource = defaultValue;
obj.myTarget = defaultValue;
end
end
Then to initialize/pre allocate an array of default TAnt objects,
ants(1,n) = TAnt(); % n is the length of your ants array