I am am new to programming and currently learning Pharo Smalltalk. However, I got to a part of the book I am using (LearningOOP) and I am stuck at "Adding a setter method". The exercise is to write the method count: such that when invoked on an instance of Counter, instance variable is set to the argument given to the message. Can anyone help please?
|c|
c := Counter new count: 7
c count 7
>>>7
In Counter class (instance side):
count: anInteger
count := anInteger
Related
I'm having trouble initializing class variables in Pharo. I started by creating a class with a single class variable:
Object subclass: #ClassVariableTestBehavior
instanceVariableNames: ''
classVariableNames: 'test'
package: 'DummyPackage'
And then on the class side I created an initialize message and set the variable to nil.
ClassVariableTestBehavior class >>> initialize
test := nil
I saved and then created an instance method:
ClassVariableTestBehavior >>> test
^ test
and went back again and changed the class method to be:
ClassVariableTestBehavior class >>> initialize
test := 34
In the playground I then printed the result of the following:
ClassVariableTestBehavior new test.
Which was nil. Why hasn't the value of the class variable updated to be 34?
The class>>initialize method is used only once, when the code is initially loaded from an external file. It does not get run every time the method is edited. (If you modified a comment would you want the data to get wiped out!?) In fact, the nil value didn't come from your method but was just the initial default value.
A common convention is to add a comment to the initialize method with a line of code that could be executed.
"
ClassVariableTestBehavior initialize.
"
I think you are confused about initialize. Based on your comment you want to create a constant value shared among the instances.
The initialize is usually a method for defining values for instance of an object you are creating. The issue with Smalltalk is that not every Smalltalk implementation runs initialize when creating an instance; so watch out for this one.
If you want to share value among instances you can create a class method like this:
ClassVariableTestBehavior class >> #test
Where:
test
"Class method returning always the same value"
^ 34
When you want to access the value you simply do:
ClassVariableTestBehavior test.
If you want to access it when you have your own instance create it you can do it like this:
| instance |
instance := ClassVariableTestBehavior new.
instance class test. "This accesses the class variable"
Edit - due to excellent comment by Leandro, which I forgot to mention.
You should use capital letter for the class variable! (credit to Leandro)
If you would like to have a value stored in a class variable instead of just constant value in a method as I have shown above. You can do it using class variable.
If you define class variable as Test, then you need to have a getter and setter for it.
You would have:
ClassVariableTestBehavior class >> #test
test
"returns value of class variable"
^ Test
ClassVariableTestBehavior class >> #test:
test: aNumber
"sets the value of class variable"
Test := aNumber
You would access the value the same way as above (just before the edit).
I try to implement a specific Dictionary class in Smalltalk which needs to override the #at:put: method in Pharo and Squeak. But when I create a class having #at:put: as an instance method and I send that methode, I get the error:
Error: Instances of '#SortedDictionary' class are not indexable
The class definition is as follows:
Dictionary subclass: #SortedDictionary
instanceVariableNames: 'index'
classVariableNames: ''
category: 'MyApplication'
An instance is created by overriding new:
!SortedDictionary class methodsFor: 'creation' stamp: 'nanitous 9/28/2015 19:17'!
new
super new.
^self initialize! !
An instance is initialized with:
initialize
index := Heap new.
^self
And the instance method is defined as:
at: anIndex put: aValue
index add: anIndex.
^self at: anIndex put: aValue! !
And I test with the script in a workspace:
| d |
d := SortedDictionary new.
d at: 1 put: 3.
I tried to make a class not derived from #Dictionary but from #Object and used an instance variable dict containing an instance of #Dictionary, but with the same result.
Why can't I override #at:put: and and how can I override this method?
EDIT
Thanks to #lurker and #aka.nice I should have done the following:
!SortedDictionary class methodsFor: 'creation' stamp: 'nanitous 9/28/2015 19:17'!
new
^super new initialize! !
Doing this wrong was outright silly! In the original and wrong code I was trying to index a nil object.
And:
!SortedDictionary instance methodsFor: 'accessing' stamp: 'nanitous 9/28/2015 19:17'!
at: anIndex put: aValue
index add: anIndex.
^super at: anIndex put: aValue! !
Well, I never came to solve this one before solving the #new issue.
Thanks again to everyone taking the trouble to help out here!
Generally, an instance of collection (more precisely a subclass of Collection) is created with #new:, not #new.
The parameter passed to new: is a size, either the size for a fixed size collection (like Array new: 3), or some pre-allocated size for variable size collection (like OrderedCollection, Set, Dictionary, ...).
From the stamp, I guess you are on a Squeak or Pharo flavour, so I will continue explanation with these dialects, it may slightly vary for other flavours.
In Squeak/Pharo, see the definition of HashedCollection class>>new:
new: nElements
"Create a Set large enough to hold nElements without growing"
^ self basicNew initialize: (self sizeFor: nElements)
It sends initialize: not initialize.
So the first thing you have to do is to define initialize: at instance side of your class, and the second thing is to remove definition of new/new: overriding these is rarely ever needed in Squeak/Pharo.
Currently you have a problem in your #new definition, when you tell self initialize what is self exactly? it is the class SortedDictionary, so you initialize the class, not the instance! And you answer the class, not the newly created instance, so you later send at:put: to the class...
It should have been something like newInstance := super new. ^newInstance initialize.
Last, your at:put: definition will loop forever, it should invoke super at: ... put: ...
A couple of nits to pick.
When you write Smalltalk code as text, such as we're doing here,
you can use the format
{classname|blank} {class|blank} >> methodHead
where the first field names the class, the second field tells whether it is class side or instance side, and the '>>' indicates start of source code.
If you don't name the class, we assume the same class as the last one named.
If you don't say it is class side, we assume it is instance side.
So your methods would be written as
SortedDictionary class>>new
^super new
initialize
>>initialize
index := Heap new
>>at: anIndex put: aValue
index add: anIndex.
^super at: anIndex put: aValue
Second, since you're defining a subclass, you only need to define
your own #new (and/or #new:) method if you must override the one that is otherwise inherited from the superclasses. (But you knew that).
Third, whenever you write an #initialize method, you want to get in the habit of writing 'super initialize.' as the first line thereof.
And once you get into said habit, you'll want to get out of the habit of writing your #new methods starting with '^super new initialize', and get into the habit of starting them with 'self basicNew initialize' instead.
I know, everyone learns to do it that other way. (Sigh.)
But that is soooooo wrong.
Extra points if you can figure out why this is so. ;-)
I need to know if it is possible to pass a Class as a parameter to a method in Smalltalk. For example,
Classname>>method: anObject
self anotherMethod: aClass with: anObject.
Classname>>anotherMethod: aClass with: anObject.
|instance|
instance:= aClass new: anObject aMessage. //supposing "new:" is the instance method of aClass.
instance aMethodFromTheClassRecieved.
Yes. Classes are just objects.
If you have:
Classname>>anotherMethod: aClass
^ aClass new.
and you execute something like:
instance anotherMethod: OrderedCollection
Then you'll get an instance of OrderedCollection.
In Smalltalk classes are objects too, so if you do OrderedCollection new you actually sent #new message to OrderedCollection class object. So you can pass classes around just like the other objects.
P.S. The main idea of Smalltalk is that it's highly dynamic and live. You can try the thing you are asking about in just 2-5 minutes, and see if it works :)
I'm a bit of a novice when it comes to Squeak Smalltalk, so I'll probably done something wrong or made an erroneous assumption about how Squeak should work. Still, I'd like to know where I went wrong...
I tried to make class Blower derived from Array. Blower is basically an Array, but with an additional instance variable called index and some methods. I thought the initialize method would be automatically run when I created a new object, and that it would initialize both the array and the index variable, but this doesn't seem to happen. If I run initialize "manually" later though, it works as expected.
Array variableSubclass: #Blower
instanceVariableNames: 'index'
(...)
Blower >> initialize
super initialize.
1 to: self size do: [ :ix | self at: ix put: ix ].
self shuffle.
index := 1.
If I do the following in a workspace:
blower := Blower new: 10.
blower inspect.
Inspect-window shows (Not what I expected):
\#( nil nil nil nil nil nil nil nil nil nil )
index: nil
If I run initialize manually, Inspect-window is correct:
blower initialize.
\#( 6 4 1 10 2 8 3 ... )
index: nil
So why doesn't initialize run when I create the Blower and set it up correctly?
Is there anyway to automate this so it happens on creation? Ie. getting initialize to work?
Look at the method ArrayedCollection class >> new. It overrides new to call new: with 0 as a parameter. This replaces the default implementation of new in Behavior which calls initialize. If you really want to do this, implement new and new: as class methods in your class. In each case, call super then call initialize.
new
^super new initialize
new: sizeRequested
^(super new: sizeRequested) initialize
Having said all that, it's a really bad idea to subclass from Array. Ask yourself "Is it reasonable for me to use a Blower any place I currently use an Array?". If not, it's not a good subclass. Any time you subclass from a collections class you're almost always doing it wrong. What you want is a class called Blower which is a subclass of Object and contains two instance variables - one for the array and one for the index. Your class will now initialize normally. For any operations you want to send to the array, write a method in the Blower to delegate it to the instance variable.
What David Buck said is correct, but there is a few more things to add, specific to Squeak:
Some collections invoke #initialize: instead of #initialize at instance creation (see for example HashedCollection)
Some other could send both #initialize then #initialize: (see SharedQueue)
But Array class>>new: has a specific implementation that completely bypasses initialize (for the sake of speed, it is known that there is nothing required for initializing an Array)
As David said, it is generally a bad idea to subclass Array, and looking at Squeak, there are too many counter-examples already.
In a previous post I explained that I was converting an old 'C' program into Objective-C, and learned the difference between messaging (old version) static methods and class methods.
However, how can I now get a class method to call an instance method (assuming it's even possible)? Here's the original (static) function:
static int newSplitB(int b, int hi, int lo, int found)
{
int hlp;
if(hi - lo <= 1)
return 0;
bIs(lo + (hi - lo + 1) / 2); // calls function bIs();
return 1;
}
and the exact same code 'translated' into Obj-C:
+(int)newSplitB :(int)b :(int)hi :(int)lo :(int)found
{
int hlp;
if((hi - lo) <= 1)
return 0;
[TablesClass bIs:(lo+(hi-lo+1)/2)]; // gives compile error
return 1;
}
The 'bIs()' function -- snipped for brevity -- is sitting in a separate source file in my 'Tables' class. Unfortunately, attempting to build the program gives me a 'TablesClass' undeclared (first use in this function) error, even though the class has been alloc/inited earlier in the same implementation file.
I've searched the net for hours for a solution, but to no avail. If what I'm trying to do isn't possible, how can I modify the last method to do what I'm after? Thanks in advance :-)
Make sure your are importing the TablesClass.h file otherwise the current class won't know of its existence and is the common issue when dealing with this error.
You said you "alloc/inited" the TableClass earlier in the same implementation file but it seems like you are using it like a static method. You don't have to alloc/init an instance of a class when you want to use its static methods. Also, if you alloc/init at instance of the TableClass, then that means you stored it somewhere that the static method newSplitB could access.
it is possible, and it is not possible, depending on how you view it:
to call an instance method, you need an instance of an object. you can send this message from a class method, if you have an object.
without an instance of the class, you can't call the instance method anywhere.
you may call class methods from anywhere (assuming it's visible in the translation).
if bIS is just a c function, then there is no need for it to be an instance method. in fact, you could leave it as a c function.
(maybe an extended example would help us understand why this must be an instance method)