I am having trouble regarding Smalltalk. I am attempting to populate an array with the numbers that are read from the file, but it doesn't seem to work. I've tried numerous options and I was hoping someone would explain to me what I'm doing wrong.
Object subclass: #MyStack
instanceVariableNames:'anArray aStack'
classVariableNames:''
poolDictionaries:''
!
MyStack class comment: 'Creates a Stack Class.'
!
!
MyStack methodsFor: 'initialize Stack'
!
new "instance creation"
^ super new.
!
init "initialization"
anArray := Array new: 32.
aStack := 0.
! !
!MyStack methodsFor: 'methods for stacks' !
pop "Removes the top entry from the stack"
| item |
item := anArray at: aStack.
aStack := aStack - 1.
!
push: x "Pushes a new entry onto the stack"
aStack := aStack + 1.
anArray at:aStack put:x.
!
top "Returns the current top of the stack"
^anArray at: aStack.
!
empty "True if the stack is empty"
^aStack = 0.
!
full "True if the stack is full"
^aStack = 32.
!
printOn: aStream "Prints entire stack one entry per line, starting the top entry"
aStream show: 'Stack:'.
aStack to:1 by:-1 do:[:i |(anArray at:i) printOn:aStream. ].
aStream show: ''
! !
"----------------------------------------------------------------------------------"
Object subclass: #IOExample
instanceVariableNames: 'input output'
classVariableNames: ''
poolDictionaries: ''
!
IOExample class comment: '
basic I/O.
'
!
!
IOExample methodsFor: 'initialize'
!
new
^ super new.
!
init
[ input := FileSelectionBrowser open asFilename readStream. ]
on: Error
do: [ :exception |
Dialog warn: 'Unable to open file'.
exception retry.
].
[ output := FileSelectionBrowser open asFilename writeStream. ]
on: Error
do: [ :exception |
Dialog warn: 'Unable to open file'.
exception retry.
].
! !
!
IOExample methodsFor: 'copy input to output turning :: into :'
!
copy
| data lookAhead theStack myStack|
[ input atEnd ] whileFalse: [
data := input next.
(data isKindOf: Integer)
ifTrue: [
(input atEnd) ifFalse: [
"myStack push: data."
lookAhead = input peek.
(lookAhead asCharacter isDigit)
ifTrue: [
]
].
].
output show: myStack.
].
input close.
output close.
! !
Did you try to run this code? If you did, I'm surprised you didn't get a compilation warning due to #2 below.
There are a number of problems in #copy (besides the fact that I don't understand exactly what it's trying to do)...
First you seems to expect the data to be numbers: data isKindOf: Integer. But then later you treat it as a stream of Characters: lookAhead asCharacter isDigit. If the first condition is true to get you past that point, the second one never can be, as you would've matched [0-9], which aren't ASCII values for digits.
lookAhead = input peek. Here you're comparing uninitialized lookAhead (nil) with the peeked value, and then throwing away the result. I assume you meant lookAhead := input peek.
Then there is the empty inner condition ifTrue: [ ]. What are you trying to do there?
Then there's the odd protocol name, 'copy input to output turning :: into :'. What does that mean, and what does that have to do with copying numbers between streams?
Justin, let me try to help you with the class MyStack and defer to another answer any comments on your example.
I've divided your code into fragments and appended my comments.
Fragment A:
Object subclass: #MyStack
instanceVariableNames:'anArray aStack'
classVariableNames:''
poolDictionaries:''
Comments for A:
A Smalltalker would have used instance variable names without indeterminate articles a or an
Object subclass: #MyStack
instanceVariableNames:'array stack'
classVariableNames:''
poolDictionaries:''
Fragment B:
MyStack class comment: 'Creates a Stack Class.'
Comments for B:
This is weird. I would have expected this instead (with no class):
MyStack comment: 'Creates a Stack Class.'
Fragment C:
MyStack methodsFor: 'initialize Stack'
new "instance creation"
^ super new.
Comments for C:*
This code puts new on the instance side of the class, which makes no sense because you usually send new to the class rather than its instances. The correct form requires adding class:
MyStack class methodsFor: 'initialize Stack'
new
^super new.
You forgot to send the initialization method (however, see Fragment D below)
new
^super new init.
Fragment D:
init "initialization"
anArray := Array new: 32.
aStack := 0.
Comments for D:
In Smalltalk people use the selector initialize so it can send super first
initialize
super initialize.
array := Array new: 32.
stack := 0.
Note that this change would require also writing new as
new
^super new initialize.
However, if your dialect already sends the initialize method by default, you should remove the implementation of new from your class.
Fragment E:
pop "Removes the top entry from the stack"
| item |
item := anArray at: aStack.
aStack := aStack - 1.
Comments for E:
You forgot to answer the item just popped out
pop
| item |
item := array at: stack.
stack := stack - 1.
^item
Fragment F:
push: x "Pushes a new entry onto the stack"
aStack := aStack + 1.
anArray at:aStack put:x.
Comments for F:
This is ok. Note however that the stack will refuse to push any item beyond the limit of 32.
push: x
stack := stack + 1.
array at: stack put: x.
Fragment G:
top "Returns the current top of the stack"
^anArray at: aStack.
empty "True if the stack is empty"
^aStack = 0.
full "True if the stack is full"
^aStack = 32.
Comments for G:
These are ok too. However, a more appropraite name for empty would have been isEmpty because all collections understand this polymorphic message. Similarly, the recommended selector for full would be isFull:
top
^array at: aStack.
isEmpty
^stack = 0.
isFull
^stack = 32.
Note also that isFull repeats the magic constant 32, which you used in the initialization code. That's not a good idea because if you change your mind in the future and decide to change 32 with, say, 64 you will have to modify two methods an not just one. You can eliminate this duplication in this way
isFull
^stack = array size.
Fragment H:
printOn: aStream
"Prints entire stack one entry per line, starting the top entry"
aStream show: 'Stack:'.
aStack to:1 by:-1 do:[:i |(anArray at:i) printOn:aStream. ].
aStream show: ''
Comments for H:
The last line of this code is superfluous and I would get rid of it. However, you may want to separate every item from the next with a space
printOn: aStream
stream show: 'Stack:'.
stack to: 1 by: -1 do:[:i |
aStream space.
(array at: i) printOn: aStream].
Related
I have a scenario where a class holds two instance variables that are mutually exclusive. That is only one can be instantiated at a time. To be precise, I have a Promise class (trying to add promises to Pharo) and it holds promiseError and promiseValue instance variables. I then want to implement the method "then: catch:".
This method should work as follows:
promiseObject := [10/0] promiseValue.
promiseObject then : [ : result | Transcript crShow : result ]
catch : [ : failure | Transcript crShow : failure ] .
I got an idea on how to implement methods that take a block as an argument from method that accepts a block and the block accepts an argument.
My attempt below will obviously not work but I have no idea on how to make it work.
then:aBlock catch: anotherBlock
|segment|
promiseValue ifNil: [ segment := promiseError ] ifNotNil: [ segment := promiseValue ].
promiseValue ifNil: [ segment := promiseValue ] ifNotNil: [ segment := promiseError ].
aBlock value:segment.
anotherBlock value: segment
This should work analogously to a try-catch block.
Have you tried something like this?
then: aBlock catch: anotherBlock
promiseError notNil ifTrue: [^anotherBlock value: promiseError].
^aBlock value: promiseValue
Note that the code does not rely on promiseValue being nil or not because nil could be a valid answer of the promise. However, if there is some promiseError, we know the promise failed, and succeeded otherwise.
Of course, here I'm assuming that this message will get sent once the promise has been successfully or unsuccessfully finished. If this is not the case, then the code should be waiting on the promise semaphore.
Basically I connected to a server with pharo using identification. Then I used Znclient to get to myserver/json file which contains a collection of key and value. How can I refresh this Json file every 40 sec without running out of memory and How can I iterate over it to collect a specific key?
This is what I did so far
" Login "
"********************************************************"
|a data|
a := ZnClient new.
a get: 'https://MyServer'.
a
headerAt: 'referer' put: 'MyServer';
formAt: 'email' add: 'myEmail';
formAt: 'password' add: 'myPassword'.
a post.
a get: 'MyServer/json'.
" get Json file "
"*******************************************************
data := NeoJSONReader fromString: a contents
You can create a loop that does the work and waits 40 seconds:
process := [ [ self shouldStillRun ] whileTrue: [
self fetchDataAndDoWork.
40 seconds asDelay wait. ] ]
forkAt: Processor userBackgroundPriority
named: '<processName>'.
Above I assume that shouldStillRun and fetchDataAndDoWork are methods in a class containing these code. If you want to play with this code in the Playground replace them with some custom snippets of code. For example:
shouldStillRun := true.
process := [ [ shouldStillRun ] whileTrue: [
| data |
'<create the client>'
data := NeoJSONReader fromString: a contents.
40 seconds asDelay wait. ] ]
forkAt: Processor userBackgroundPriority
named: '<processName>'.
As long as you do not store all the data return by each call you should not have a memory problem.
If your data represents a dictionary then NeoJSON will return a dictionary object, and you can just use the at: message to get the value. You can inspect the data object to see what you get back.
I meant using the block do: every: of the class TaskScheduler. will that also work?
scheduler := TaskScheduler new.
scheduler start.
"refresh every 40 seconds"
scheduler
do: [a get: 'https://MyServer/json'.
Transcript show: 'Refreshing......'; cr.
data := NeoJSONReader fromString: a contents; cr.
every: 60 seconds
I am fairly new to Smalltalk and I'm stuck on how to print elements from a stack. I have two classes, one which creates the stack using OrderedCollection, which works, and a second class (Object subclass). For the second class I have two instance variables name and weight(with set and get methods). I need to make two more methods print and printSpecial. Print output the name and weight to the Transcript on the same line using the get method from name but cannot use the get method from weight. PrintSpecial is similar to print but the weight must be < 100. I have tried doing print and printScpecial but cannot figure it out. Below is what I have so far. Any help would be appreciated.
name: a
name := a
name
^name
print
[ Transcript
show: weight;
show: name;
cr ]
printSpecial
[ weight <= 100 ]
whileTrue: [ Transcript
show: weight;
show: name;
cr ]
Both your print and printSpecial methods enclose their bodies in squared brackets. You should remove them. Try:
print
Transcript
show: weight;
show: name;
cr
printSpecial
weight <= 100 ifTrue: [
Transcript
show: weight;
show: name;
cr]
Notice that in printSpecial I've replaced whileTrue: with ifTrue:. The reason is that you don't want to keep printing for ever if the weight happens to meet the condition.
Another thing I would suggest is to avoid repeating code. So, I would propose this:
printSpecial
weight <= 100 ifTrue: [self print]
This way, if you later decide to improve print you won't have to copy the improvement to printSpecial.
Finally, you say you have a collection of these objects. Therefore you should have some way of enumerating them (e.g., via do:). Thus, if the actual request consisted in printing them all you should implement print and printSpecial in the elements' class and then implement the same messages in your Stack class.
Stack >> print
collection do: [:elem | elem print]
Stack >> printSpecial
collection do: [:elem | elem printSpecial]
where I'm assuming that the instance variable that holds your elements is named collection.
Even better. You could implement do: in your Stack class and then use self do: instead of collection do: as I did above. Something on the lines of
Stack >> do: aBlock
collection do: aBlock
and then
Stack >> print
self do: [:elem | elem print]
Stack >> printSpecial
self do: [:elem | elem printSpecial]
Example:
st> [ fnord := 7 ] value
I was always under the impression that they went into the SystemDictionary at Smalltalk, but that's not true:
st> [ fnord := 7 ] value
st> Smalltalk at: #fnord
Object: SystemDictionary new: 512 "<0x2acfca382030>" error: Invalid argument #fnord: key not found
However, at least on GNU Smalltalk, the values seem to be persisted somewhere --- accessing fnord returns the right value:
st> [ fnord := 7 ] value
st> fnord
7
Update: I figured out how to disassemble blocks! It's really hard.
st> [ fnord := 7 ] block inspect
An instance of CompiledBlock
header: 32768
clean-ness flags: 0
number of arguments: 0
number of temporaries: 0
number of literals: 4
needed stack slots: 8
method: UndefinedObject>>executeStatements
literals: [
[1] {fnord}
[2] a BlockClosure
[3] #block
[4] #inspect
]
byte codes: [
[1] source code line number 1
[3] push 7
[5] store into Global Variable {fnord}
[7] pop stack top
push Global Variable {fnord}
[9] return stack top
]
[] in UndefinedObject>>executeStatements
So it definitely thinks it's writing to a global variable.
Undeclared variable bindings go into a global dictionary named Undeclared. That binding (a key->value pair) gets moved to Smalltalk once you properly declare it. This is how forward-references are resolved when loading code, for example. That is, when a variable is used before the code declaring it is loaded.
Here is basically what I want to do (I have implemented the Stream sub-class WordStream):
| aStream |
aStream := WordStream on: 'Test My Word Stream Class!'.
self assert: (aStream next) = 'Test'.
self assert: (aStream next) = 'My'.
self assert: (aStream next) = 'Word'.
self assert: (aStream next) = 'Stream'.
self assert: (aStream next) = 'Class'.
self assert: aStream atEnd
I've got everything stored correctly in my stream, but I can only figure out how to get the next word by doing:
next
|tmpStream|
tmpStream := ReadStream on: myStream contents.
^tmpStream nextDelimited: Character space.
This works for only the first word (obviously) - when I try to use my instance variable myStream, it just keeps returning ''.
i.e., I can't get this to work:
next
^myStream nextDelimited: Character space.
Could someone give me a hand?
edit:
This is how I implemented on:
on: inString
|tmpStream|
myStream := ReadWriteStream on: String new.
tmpStream := ReadStream on: inString.
[ tmpStream atEnd ] whileFalse: [ myStream nextPutAll: (tmpStream nextDelimited: Character space); nextPut: $ ].
PositionableStream defines #nextDelimited: already, but that uses #next. One fairly horrible thing you could do is copy PositionableStream >> #nextDelimited:'s implementation, and replace the self next call with your own get-a-single-character method.