Print a SortedCollection to screen using keysAndValueDo (smalltalk) - smalltalk

Hello i'm learning and am new to smalltalk and I'm trying to print my SortedCollection to screen trying to use keysAndValueDo but im not sure how its done, if anyone could give me a general example that would be great

Part 1 - Displaying to the screen
The most straight forward way to print to the screen in a GUI-based Smalltalk is to use the message:
Transcript show: 'some text'
(The Transcript is a system object that displays into a scrollable window).
To make sure there is a newline before each line of display text, we send the message cr to the Transcript
Transcript cr.
Transcript show: 'some text'.
A shorthand method, that saves us re-typing Transcript over and over, is to send Transcript a series of messages one after another. This is called a message cascade. Each time we end a message in ; it means send to the same receiver as the last message was sent to.
We can then shorten this again, as Smalltalk pays no attention to newlines in expressions.
The final display message cascade becomes:
Transcript cr; show: 'some text'.
Part 2: Enumerating aSortedCollection using keysAndValuesDo:
This keyword message is SequencableCollectionand its method header is:
keysAndValuesDo: aBlock
"Enumerate the receiver with all the keys (aka indices) and values."
(It works the same way in Dolphin, and in Squeak and its derivatives, Pharo and Cuis).
The keyword message keysAndValuesDo: takes a block argument.
A block is an anonymous object, with one method. Its method is defined between a matched pair of square brackets - a [ ... ] pair.
In this case, we need a local variable in the block for the key of each element of the collection, and another local variable for the value of each element.
We can call them anything we like, and in this case, it is the order that they appear in that is important. keysAndValuesDo: will put the element's key into the first local variable in the block, and will put the element's value into the second local variable in the block.
Local variables in a block are declared at the start of the block, and each variable name is identified by prefixing it with :. The local variable declarations are ended with a |.
The block then looks like
[:local1 :local2 |
"do something for each element, with the key in local1 and the value in local2"
]
I prefer meaningful local variable names, so I'll use eachKey and eachValue.
Part 3: Putting it all together
To enumerate through mySortedCollection
"Declare the mySortedCollection variable"
|mySortedCollection|
"Initialise the collection"
mySortedCollection := SortedCollection new.
"add in some data to the collection"
mySortedCollection add: ('First') ;
add: ('Second') ;
add: ('Third').
"Enumerate through the collection, displaying to the Transcript window"
mySortedCollection keysAndValuesDo:
[:eachKey :eachValue |
Transcript cr; show: eachKey; show: ' '; show: eachValue
] .
Paste the code into a Workspace (known as a Playground in Pharo, from version 4.0 onwards). Select the text. Once selected, right-click (on a two or three button mouse) and select "Do it" from the menu. Or use Ctrl-d as a keyboard shortcut. (The exact chording key may vary on your platform)
Final notes
In a SortedCollection or an OrderedCollection, the key is the index. The value is what is stored at element[index].
In a Dictionary, the key of the element is the key, and the value of the element is the value.
SortedCollections are sorted in order of the element values, according to the definition of the collections sort block. In the absence of a custom sort block, they will be added in ascending order. 'First', 'Second' and 'Third' are, coincidentally, in alphabetical order. It happens to work out nicely in this example.

The following example works with Pharo Smalltalk, other Smalltalk implementation might work similar.
First, look at existing print methods as examples. In case of SortedCollection, you find them in the printing protocol of its superclass Collection.
You will find that the printing of elements is defined in printElementsOn:. So you could overwrite this method in SortedCollection.
Here is a printElementsOn: method that will use keysAndValuesDo:, as you were asking for:
printElementsOn: aStream
aStream nextPut: $(.
self keysAndValuesDo: [:key :value |
aStream
nextPut: $(;
print: key;
nextPut: $:;
space;
print: value;
nextPut: $)].
aStream nextPut: $)
Now a collection that before printed:
"a SortedCollection(1 2 3 3 5 10)"
will print:
"a SortedCollection((1: 1)(2: 2)(3: 3)(4: 3)(5: 5)(6: 10))"

Related

What exactly happened when not using "copy" to deal with a string?

The purpose of the function below is to return a string having inserted the argument value between two stars.
star-name: func [name /local stars] [
stars: "**"
insert next stars name
stars
]
print star-name "test" ;*test*
print star-name "this" ;*thistest*, but what I really want is *this*
The second time I call the function, the argument of the first call still remains inserted. I know the answer is to use copy "**".
My question is, doesn't it reassign the stars variable to "**" every time the function is called?
In the case of the function there is just one "**" string definition. That definition is used by Rebol load function just once, since load is run just once to translate the code to Rebol internal form - a block. It is true that the assignment occurs twice if you call the function twice, but the assignment does not create anything, it just makes the variable refer to the same string again.
In your comment you should notice that you actually have two "**" string definitions leading to two strings being created by load. If you use
code: [stars: "**" insert next stars something]
something: "this"
do code
something: "that"
do code
you will notice that there is just one string definition and while you do not have any function the behaviour is the same as it was when a function was used.
If you use a set-word on a series, then the default behavior is to allocate the memory for that series just the once. This allows you to use that as a static variable that persists between function calls as you have found.
If you don't want that behavior, then you need to explicitly copy the series to create a new series each time.
This is another way you can do this as the stars local is not required
star-name: func [ name ][
rejoin [ "*" name "*" ]
]

Smalltalk won't recognize declared temporary variables

So I'm a complete fledgling when it comes to Smalltalk and right now I'm writing a very simple app with a GUI. All this app does is add two operands together from two input fields and displays the sum in a third, read-only input field.
I am having trouble with VisualWorks recognizing temporary variables that I have already declared.
I try to highlight any line with a declared temporary variable, and it will say such variable has not been declared; do I want to declare it as temp, instance, shared, etc... It's especially strange because the method can be accepted and even read through when I run it by the GUI,(although I am having a problem typecasting the variables as integers) but if I want to print or inspect any line with a declared temporary variable, it will say that it doesn't recognize it as such and do I want to declare it as this or that.
The Code:
add
"adds two input fields"
| op1 op2 result |
op1 := #InputOperand1 value asInteger.
op2 := #InputOperand2 value asInteger.
result := op1 + op2.
^result
Any ideas?
The problem is that only the text you've selected is compiled and evaluated. If you are only selecting a single line, then the variable declarations aren't included in the compiled code. If you select the whole method body for evaluation (not including the method signature), it should work fine. Another option is to just choose "create temp" when the compiler prompts, and then revert to the saved version of the method to get rid of the extra temp declaration.

Explain a piece of Smalltalk code?

I cannot understand this piece of Smalltalk code:
[(line := self upTo: Character cr) size = 0] whileTrue.
Can anybody help explain it?
One easy thing to do, if you have the image where the code came from, is run a debugger on it and step through.
If you came across the code out of context, like a mailing list post, then you could browse implementers of one of the messages and see what it does. For example, #size and #whileTrue are pretty standard, so we'll skip those for now, but #upTo: sounds interesting. It reminds me of the stream methods, and bringing up implementors on it confirms that (in Pharo 1.1.1), ReadStream defines it. There is no method comment, but OmniBrowser shows a little arrow next to the method name indicating that it is defined in a superclass. If we check the immediate superclass, PositionableStream, there is a good method comment explaining what the method does, which is draw from the stream until reaching the object specified by the argument.
Now, if we parse the code logically, it seems that it:
reads a line from the stream (i.e. up to a cr)
if it is empty (size = 0), the loop continues
if it is not, it is returned
So, the code skips all empty lines and returns the first non-empty one. To confirm, we could pass it a stream on a multi-line string and run it like so:
line := nil.
paragraph := '
this is a line of text.
this is another line
line number three' readStream.
[(line := paragraph upTo: Character cr) size = 0] whileTrue.
line. "Returns 'this is a line of text.'"
Is this more readable:
while(!strlen(line=gets(self)))
Above expression has a flaw if feof or any other error, line==NULL
So has the Smalltalk expression, if end of stream is encountered, upTo: will answer an empty collection, and you'll have an infinite loop, unless you have a special stream that raises an Error on end of stream... Try
String new readStream upTo: Character cr
The precedence rules of Smalltalk are
first: unary messages
second: binary messages
third: keyword messages
last: left to right
This order of left to right, can be changed by using parenthesis i.e. ( ) brackets. The expression within the pair of brackets is evaluated first.
Where brackets are nested, the inner-most bracket is is evaluated first, then work outwards in towards the outer bracket, and finally the remains of the expression outside the brackets.
Because of the strong left-to-right tendency, I often find it useful to read the expression from right to left.
So for [(line := self upTo: Character cr) size = 0] whileTrue.
Approaching it from the end back to beginning gives us the following interpretation.
. End the expression. Equivalent to ; in C or Java
whileTrue What's immediately to the left of it? ] the closure of a block object.
So whileTrue is a unary message being sent to the block [ ... ]
i.e. keep doing this block, while the block evaluates to true
A block returns the result of the last expression evaluated in the block.
The last expression in the block is size = 0 a comparison. And a binary message.
size is generally a unary message sent to a receiver. So we're checking the size of something, to see if it is 0. If the something has a size of 0, keep going.
What is it we are checking the size of? The expression immediately to the left of the message name. To the left of size is
(line := self upTo: Character cr)
That's what we want to know the size of.
So, time to put this expression under the knife.
(line := self upTo: Character cr) is an assignment. line is going have the result of
self upTo: Character cr assigned to it.
What's at the right-hand end of that expression? cr It's a unary message, so has highest precedence. What does it get sent to. i.e. what is the receiver for the cr message?
Immediately to its left is Character. So send the Character class the message cr This evaluates to an instance of class Character with the value 13 - i.e. a carriage return character.
So now we're down to self upTo: aCarriageReturn
If self - the object receiving the self upTo: aCarriageReturn message - does not understand the message name sizeUpto: it will raise an exception.
So if this is code from a working system, we can infer that self has to be an object that understands sizeUpTo: At this point, I am often tempted to search for the massage name to see which Classes have the message named sizeUpto: in their list of message names they know and understand (i.e. their message protocol ).
(In this case, it did me no good - it's not a method in any of the classes in my Smalltalk system).
But self appears to be being asked to deal with a character string that contains (potentially) many many carriage returns.
So, return the first part of aCharacterString, as far as the first carriage-return.
If the length of aCharacterString from the start to the first carriage return is zero, keep going and do it all again.
So it seems to be we're dealing with a concatenation of multiple cr-terminated strings, and processing each one in turn until we find one that's not empty (apart from its carriage- return), and assigning it to line
One thing about Smalltalk that I'm personally not a huge fan of is that, while message passing is used consistently to do nearly everything, it can sometimes be difficult to determine what message is being sent to what receiver. This is because Smalltalk doesn't have any delimiters around message sends (such as Objective-C for example) and instead allows you to chain message sends while following a set of precedence rules which go something like "message sends are interpreted from left to right, and unless delimited by parentheses, messages with many keywords are evaluated first, then binary keyword messages, then unary, and then no keyword ones." Of course using temporary variables or even just parentheses to make the order of the messages explicit can reduce the number of situations where you have to think about this order of operations. Here is an example of the above code, split up into multiple lines, using temp variables and parenthesis for explicit message ordering for readability. I think this is a bit clearer about the intent of the code:
line = (self upTo: (Character cr)).
([((line size) = 0)] whileTrue).
So basically, line is the string created when you concatenate the characters in string self up until the carriage return character (Character cr).
Then, we check line's size in characters, and check if that's equal to 0, and because we put this in a block (brackets), we can send it a whileTrue, which re-evaluates the condition in the block until it returns true. So, yeah whileTrue really would be clearer if it was called doWhileTrue or something like that.
Hope that helps.

Smalltalk, displaying OrderedCollection to List Widget

Hi I have an ordered collection of strings which I'm trying to display on a list widget.
I do the following:
self displayWidget list: coll.
where displayWidget is a List Widget and coll is the OrderedCollection containing the strings. It will display it, but it displays it in a single line.
Instead of getting
line one
line two
line three
I get:
line oneline twoline three
I'm using visual works.*
Inside list: you probably want something similar to
coll do: [:element | Transcript show element; cr]
When you send do: [:e | ...] to a collection it evaluates the block once for each element in the collection, each time passing the element into element.
Each time I'm sending cr to Transcript to add a carriage return after each element.
You can iterate the collection and send withCRs message to the Strings.
Here is an simple example:
| i |
i:= 0.
[i < 5] whileTrue: [ Transcript show: 'Hello world.\' withCRs.
i := i +1.
]
withCRs method replace each \ ocurrence for a new line and carry return.
Hope it helps you.

Working with Seaside continuations

How do I get a BlockClosure in Squeak (I want to use BlockClosure>>callCC)?
When I write [#foo] that is a BlockContext, what's the deal?
Update: I have worked out that BlockClosure is a thing mainly of new compiler.
Instead how do I work with seaside Continuations? I'm having problems, and any examples would be appreciated.
Further update: The purpose of this is not to use seaside (at least not directly) but rather to write traversals and other such things in a way that is easier than rolling my own state-tracking iterators.
Normally, with Seaside, you never have to deal with Continuations yourself at all.
You just use #call: and #answer: from within your components.
If you're trying to do something else with Continuation other than writing a Seaside application, take a look at WAComponent>>call: for an example of usage.
Or try this. Open a Transcript window. Now, in a Workspace, select all of this code at once and Do-it:
continuation := nil.
result := Continuation currentDo: [:cc |
"store the continuation, cc, somewhere for later use"
continuation := cc.
1 ].
Transcript show: result.
You should see 1 displayed in the Transcript window. Now, in the workspace, do:
continuation value: 2
and then:
continuation value: 3
You should see each value you pass to continuation displayed in the Transcript because each value you pass to #value: causes the context of the continuation to be restored and the new value assigned to result.
Hopefully that helps...