Can't seem to get output in the transcript [Smalltalk] - oop

I'm currently working on an assignment done in smalltalk and while I'm just getting the grasp of the language, I'm a bit stumped and am looking for some direction.
Normally to print to the transcript in Pharo I would use:
print
Transcript show: 'Hello my name is: ' , self theName printString; cr
It seems that if I put a conditional in the beginning such as
printNew
(age < 50) ifTrue: [ Transcript show: 'Hello my name is: ', self theName, 'and I am old' printString ]; cr
I can't seem to get the printNew method to print to the transcript. Thank you in advance!

Let me format your expression to better understand it:
(age < 50)
ifTrue: [
Transcript show:
'Hello my name is: ', self theName, 'and I am old' printString];
cr
looks like
(age < 5) ifTrue: [<something>]; cr
which in turn has the structure of
(age < 5) msg; cr
because in Smalltalk ifTrue: [<something>] is nothing but a message. Do you see the mistake now? The cascade symbol ; sends first msg to the Boolean expression (age < 5) and then sends it cr which it does not understand. Just fix the transposition between ] and ; cr:
(age < 50)
ifTrue: [
Transcript show:
'Hello my name is: ', self theName, 'and I am old' printString;
cr]
Note also that a better way to write the same is
(age < 50)
ifTrue: [
Transcript
show: 'Hello my name is: ';
show: self theName;
show: ' and I am old' printString;
cr]
Why? Because this way you do not create two intermediate strings by concatenating the three parts of your text.
Finally note that because of precedence rules you don't need the parentheses around age < 50
BTW, if you are under 50 you are not old!

Related

Smalltalk - is there something similar to && from C?

I have to write a pure-object Smalltalk program, in which I need to evaluate conditions until one of them fails. I know that in C, we can use the && operator for this, and conditions only get evalutated if necessary.
Is there something similar in Smalltalk?
Conditional "anding" can be achieved by using the & message, or the and: message.
firstBooleanExpression & secondBooleanExpression
ifTrue: [ 'do something' ].
Using & as show above, the second part of the condition (secondBooleanExpression) is evaluated regardless of whether the first half evaluates to true or false.
(firstBooleanExpression and: [secondBooleanExpression])
ifTrue: [ 'do something' ].
Using and:, on the other hand, the second part is only evaluated if the first half evaluates to true. Typically you'd use this form, unless you explicitly wanted to evaluate the second half.
The same principle applies to or:.
If I understand your question, you're looking for something like this:
[ <condition> ] whileTrue: [ <loop body> ].
#whileTrue: is not a keyword of course and you could implement it yourself (look at the implementation in your Smalltalk of choice and be enlightened :)).
If you don't need a loop but are simply looking for a way to express conditionals then #ifTrue:, #ifFalse:, #ifTrue:ifFalse: and #ifFalse:ifTrue: are your friends. Examples:
myCollection isEmpty ifTrue: [ Transcript open; show: 'empty'; cr ].
myCollection isEmpty ifFalse: [ Transcript open; show: 'not empty' cr ].
myBoolean
ifTrue: [ Transcript open; show: 'true'; cr ]
ifFalse: [ Transcript open; show: 'false'; cr ].

Printing from an OrderedCollection in Smalltalk

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]

Creating a Caesar Cipher Method

So I need to get the Caesar Cipher code in smalltalk and create a method and use it so I can do the following test on it
|aString|
aString:=Caesar new encrypt: 'CAESAR'.
Transcript show: aString.
I already have the class made. But I need to make the method of it.
I found this but how can I make a method out of this so I can all the above code in playground.
| i c strCipherText strText iShiftValue iShift |
strText := 'the quick brown fox jumps over the lazy dog'.
iShiftValue := 3.
strCipherText := ''.
iShift := iShiftValue \\ 26.
i := 1.
[ i <= (strText size) ]
whileTrue: [
c := (strText at: i) asUppercase.
( ( c >= $A) & ( c <= $Z ) )
ifTrue: [
((c asciiValue) + iShift > $Z asciiValue)
ifTrue: [
strCipherText := strCipherText, (((c asciiValue) + iShift - 26)
asCharacter asString).
]
ifFalse: [
strCipherText := strCipherText, (((c asciiValue) + iShift)
asCharacter asString).
].
]
ifFalse: [
strCipherText := strCipherText, ' '.
].
i := i + 1.
].
Transcript show: strCipherText.
Transcript cr.
So to make thing clear, I need to make a method using the Caesar Cipher code and use the "aString" code at the beginning and test it with that. I have this code above but this has already text in it and can't be put into the method.
Any help will be appreciated.
As Max said in his comment the code above can be put in a method. The only missing part is a first line with the selector and the formal argument:
caesarCipherOf: strText
<insert the code here>
Another good suggestion by Max is to call the argument aString rather than strText because that's more aligned with how Smalltalkers name things.
But now let's take a look at the source code itself:
The comparison c >= $A & (c <= $Z) means c isLetter.
The conditional calculation of the next character means that we want to shift-rotate c by moving it 3 characters to the right, wrapping it around if it gets beyond $Z. This can be easily expressed as:
(c codePoint - 64 + 3 \\ 26 + 64) asCharacter
where 64 = $A codePoint - 1, is the offset between $A and any given uppercase character c. Note also that I've replaced asciiValue with codePoint.
With these two observations the method can be re-written as
caesarCipherOf: aString
^aString collect: [:c |
c isLetter
ifTrue: [(c asUppercase codePoint - 64 + 3 \\ 26 + 64) asCharacter]
ifFalse: [$ ]]
This is not only shorter, it is more efficient because it avoids creating two new instances of String at every character. Specifically, any expression of the form
string := string , <character> asString
creates two Strings: one as the result of sending #asString, another as the result of sending the concatenation message #,. Instead, #collect: creates only one instance, the one that the method returns.

smalltalk block - can I explicitly set the returning value and stop executing the block?

The return value of #value: message, when sent to a block, is the value of the last sentence in that block. So [ 1 + 2. 3 + 4. ] value evaluates to 7.
I find that hard to use sometimes. Is there a way to explicitly set the returning value and stop executing the block?
For exercise, try rewriting this block without using my imaginary #return: message and see how ugly it gets. I must be missing something.
[ :one :two |
one isNil ifTrue: [ two isNil ifTrue: [ self return: nil ] ifFalse: [ self return: true ] ].
two ifNil: [ self return: false ].
(one > two)
ifTrue: [ self return: true ]
ifFalse: [ (one < two)
ifTrue: [ self return: false ]
ifFalse: [ self return: nil ]
].
]
EDIT: self return: sth really is nonsense, but it does make sense at some level :)
There's nothing like a guard clause - blah ifTrue: [^ foo] - inside a block, because ^ is a non-local return, returning from the method calling the block rather than the block itself.
Big blocks - like big anythings - should be refactored into smaller, more understandable/tractable subparts, but sometimes that's not always possible. I mean this answer to suggest options to try when you can't really simplify in the usual ways.
If your block is really that complicated, and you can't get it simpler (splitting it up delocalises the information too much, for instance) then perhaps you can use an explicit return value. In particular, if your block doesn't return nil you could do something like
[:one :two | | result |
result := (one isNil and: [two isNil]) ifTrue: [false].
result ifNil: ["do one thing, possibly setting result"].
result]
If your block can return nil, you'll need another sentinel value:
[:one :two | | result marker |
result := marker := Object new.
(result == marker) ifTrue: ["do one thing, possibly setting result"].
result]
Lastly - and I hesitate to suggest this - you could do this:
[1 + 2.
thisContext return: 5.
3 + 4] value
which returns 5.
(Verifying how this interacts with ^ and inlined selectors like #ifTrue:ifFalse: left as an exercise for the reader.)
It seems that your code tries to handles nil like an infinity value when comparing one and two. The following code may be more readable depending on the context:
a := [:one :two |
| x y |
x := one ifNil: [Float infinity].
y := two ifNil: [Float infinity].
(x = y) ifTrue: [nil] ifFalse: [x > y]]
A useful feature of #ifTrue:ifFalse:, #ifNil:ifNotNil: and similar testing methods is that they return the value of the block that gets evaluated. e.g. (4 > 1) ifTrue: ['greater'] ifFalse: ['not-greater'] evaluates to 'greater'. This feature often makes it possible to return a value from a nested block in tail position.
When the code inside a block gets too complicated I suggest your refactor it to a method. But see Frank's answer for workarounds.
Edit:
As pointed out in the comments the code above assumes numbers. I also came up with something that works with other comparable objects:
a:=
[ :one :two |
true caseOf: {
[one = two]->[nil].
[one isNil]->[true].
[two isNil]->[false]
} otherwise: [one>two]]
That #caseOf: construct is rarely used but it's certainly better than thisContext return:
You'd like to implement some break, continue, exit...
The usual way to control flow in Smalltalk is with blocks.
So one funny solution is to use a helper method with a Block return value to break the flow, like described here .
Object>>exitThru: aBlock
^aBlock value: [:result | ^result]
Now, let see how to use it:
| aBlock |
aBlock := [ :one :two |
self exitThru: [:exit |
one isNil ifTrue: [ two isNil ifTrue: [exit value: nil ] ifFalse: [ exit value: true ] ].
two isNil ifTrue: [ exit value: false ].
one > two ifTrue: [ exit value: true ].
one < two ifTrue: [ exit value: false ].
exit value: nil] ].
#(('abc' nil) (nil nil) (nil 'def') ('y' 'abc') ('y' 'y') ('y' 'z'))
collect:
[:pair |
aBlock value: pair first value: pair last ]
-> #(false nil true true nil false)
EDIT my first version was unnecessarily complex, can't remember what lead me to an additional indirection:
| aBlock |
aBlock := [:wrapOne :wrapTwo |
self exitThru: [:exit |
[ :one :two |
one isNil ifTrue: [ two isNil ifTrue: [exit value: nil ] ifFalse: [ exit value: true ] ].
two isNil ifTrue: [ exit value: false ].
one > two ifTrue: [ exit value: true ].
one < two ifTrue: [ exit value: false ].
exit value: nil ]
value: wrapOne value: wrapTwo ] ].
Well, more funny than usefull, I hope you will find more simple and expressive way to code.

string to integer in smalltalk

i want to convert the input value in "Prompter prompt: aStringPrompt" into a integer value, how can i do that?
Two steps: (a) validate the input, and (b) convert.
You could validate like so: myString isAllDigits.
Converting is trivial: '1' asInteger. In Squeak, at least, this returns the integer 1. 'g1' asInteger returns 1, as does 'g1' asInteger. g asInteger returns nil.
So in summary:
"Given some input string s containing a decimal representation of a number, either return s in integer form, or raise an exception."
s := self getUserInput.
(s isAllDigits) ifFalse: [ Exception signal: '"', s, '" is not a (decimal) number' ].
^ s asInteger.
Just tried this in Dolphin 6:
(Prompter prompt: 'Enter a number') asInteger
Run this (place cursor on the above in a workspace and hit Ctrl-D), enter 123 in the prompt that comes up, and you'll see 123 displayed as the output. If you remove the #asInteger invocation, it'll display '123', indicating that a String was returned.
As to your 'does not understand #number', that means that somewhere in the code you were running the message #number as being sent to an object that didn't know how to handle it.
For the fun of it I took your code and slightly reformatted it:
| dir |
[ dir isNil or: [ dir isEmpty ] ] whileTrue:
[ dir:= Prompter prompt: 'Enter your number' caption: 'Input the Number' ].
MessageBox notify: 'your inputed number is ', (dir) caption: 'Inputed'.
and found that it ran just fine. Then I noticed it didn't convert the returned String to a number, so I changed it to:
| dir |
[ ( dir isNil or: [ dir isEmpty ] ) or: [ (dir select: [ :c | c isDigit not ]) size > 0 ] ] whileTrue:
[ dir:= Prompter prompt: 'Enter your number' caption: 'Input the Number' ].
MessageBox notify: 'your inputed number is ', (dir) caption: 'Inputed'.
This also ran fine, with the added benefit that it won't accept non-numeric characters.
Share and enjoy.