Conditional swapping of items in an array - smalltalk

I have a collection of items of type A, B, and C.
I would like to process the collection and swap all A and B pairs, but if there is C (which is also a collection), I want to process that recursively.
So
#(A1 A2 B1 B2 A3 #(A4 A5 B3) )
would be translated into
#(A1 B1 A2 B2 A3 #(A4 B3 A5) )
The swap isn't transitive so #(A1 B1 B2) will be translated into #(B1 A1 B2) and not #(B1 B2 A1).
I wanted to use overlappingPairsDo: but the problem is that the second element is always processed twice.
Can this be achieved somehow with Collection API without resorting to primitive forloops?
I am looking for readable, not performant solution.

I think my solution below should do what you're after, but a few notes up front:
The "requirements" seem a bit artificial - as in, I'm having a hard time imagining a use case where you'd want this sort of swapping. But that could just be my lack of imagination, of course, or due to your attempt to simplify the problem.
A proper solution should, in my opinion, create the objects required so that code can be moved where it belongs. My solution just plunks it (mostly) in a class-side method for demonstration purposes.
You're asking for this to be "achieved somehow with [the] Collection API without resorting to primitive for[-]loops" - I wouldn't be so quick to dismiss going down to the basics. After all, if you look at the implementation of, say, #overlappingPairsDo:, that's exactly what they do, and since you're asking your question within the pharo tag, you're more than welcome to contribute your new way of doing something useful to the "Collections API" so that we can all benefit from it.
To help out, I've added a class SwapPairsDemo with two class-side methods. The first one is just a helper, since, for demonstration purposes, we're using the Array objects from your example, and they contain ByteSymbol instances as your A and B types which we want to distinguish from the C collection type - only, ByteSymbols are of course themselves collections, so let's pretend they're not just for the sake of this exercise.
isRealCollection: anObject
^anObject isCollection
and: [anObject isString not
and: [anObject isSymbol not]]
The second method holds the code to show swapping and to allow recursion:
swapPairsIn: aCollection ifTypeA: isTypeABlock andTypeB: isTypeBBlock
| shouldSwapValues wasJustSwapped |
shouldSwapValues := OrderedCollection new: aCollection size - 1 withAll: false.
aCollection overlappingPairsWithIndexDo: [:firstItem :secondItem :eachIndex |
(self isRealCollection: firstItem)
ifTrue: [self swapPairsIn: firstItem ifTypeA: isTypeABlock andTypeB: isTypeBBlock]
ifFalse: [
shouldSwapValues at: eachIndex put: ((self isRealCollection: secondItem) not
and: [(isTypeABlock value: firstItem)
and: [isTypeBBlock value: secondItem]])
]
].
(self isRealCollection: aCollection last)
ifTrue: [self swapPairsIn: aCollection last ifTypeA: isTypeABlock andTypeB: isTypeBBlock].
wasJustSwapped := false.
shouldSwapValues withIndexDo: [:eachBoolean :eachIndex |
(eachBoolean and: [wasJustSwapped not])
ifTrue: [
aCollection swap: eachIndex with: eachIndex + 1.
wasJustSwapped := true
]
ifFalse: [wasJustSwapped := false]
]
That's a bit of a handful, and I'd usually refactor a method this big, plus you might want to take care of nil, empty lists, etc., but hopefully you get the idea for an approach to your problem. The code consists of three steps:
Build a collection (size one less than the size of the main collection) of booleans to determine whether two items should be swapped by iterating with overlappingPairsWithIndexDo:.
This iteration doesn't handle the last element by itself, so we need to take care of this element possibly being a collection in a separate step.
Finally, we use our collection of booleans to perform the swapping, but we don't swap again if we've just swapped the previous time (I think this is what you meant by the swap not being transitive).
To run the code, you need to supply your collection and a way to tell whether things are type "A" or "B" - I've just used your example, so I just ask them whether they start with those letters - obviously that could be substituted by whatever fits your use case.
| collection target isTypeA isTypeB |
collection := #(A1 A2 B1 B2 A3 #(A4 A5 B3) ).
target := #(A1 B1 A2 B2 A3 #(A4 B3 A5) ).
isTypeA := [:anItem | anItem beginsWith: 'A'].
isTypeB := [:anItem | anItem beginsWith: 'B'].
SwapPairsDemo swapPairsIn: collection ifTypeA: isTypeA andTypeB: isTypeB.
^collection = target
Inspecting this in a workspace returns true, i.e. the swaps on the collection have been performed so that it is now the same as the target.

Here's a solution without recursion that uses a two step approach:
result := OrderedCollection new.
#(1 3 4 6 7)
piecesCutWhere: [ :a :b | a even = b even ]
do: [ :run |
result addAll: ((result isEmpty or: [ result last even ~= run first even ])
ifTrue: [ run ]
ifFalse: [ run reverse ]) ].
result asArray = #(1 4 3 6 7) "--> true"
So first we split the collection wherever we see the possibility for swapping. Then, in the second step, we only swap if the last element of the result collection still allows for swapping.
Adding recursion to this should be straight forward.

Related

How can I store a code block in a variable and call it and get its return value whenever needed?

I'm making a little text adventure in Smalltalk. It's made up of "screens" that have their texts and choices for other screens included. Since I want the game to be dynamic, I also want to include branching. For instance, if the player is at a blacksmith and wants to buy an axe, the screen the player goes to immediately checks if the player has enough money and jumps to one of two other screens based on that.
I already have this working: The screens (classes named Place) have a list where the first item is the function and the following items are the arguments. However, I have it done in a very ugly way: the first item is a string that is then compared against in a big "action" method, so it looks something like this:
game data method:
blacksmith := Place new.
blacksmith choiceText: 'I would like an axe.';
blacksmith action add: 'money'; add: 20; add: blacksmith_good; add: blacksmith_bad.
action method: (currentScreen is also a Place; the class also contains a BranchMoney method that does the actual decision making)
(currentScreen action at: 1) = 'money'
ifTrue: [
currentScreen := (currentScreen BranchMoney)
]
That's obviously not ideal, and I would like to compact it by doing something like this:
game data method:
blacksmith action add: [blacksmith BranchMoney]; add: 20; add: blacksmith_good; add: blacksmith_bad.
action method:
currentScreen := (currentScreen action at: 1)
So that instead of string checking the game would just directly proceed with the method I want it to do.
However, it doesn't seem to work - I've tried different changes to the code, and the problem seems to be that the currentScreen := (currentScreen action at: 1) line just replaces the contents of currentScreen with the code block contents – it doesn't calculate the block's contents and use its resulting value that is of type Place.
I've tried using round brackets in the game data method – that throws a list out of bounds exception, because it tries to calculate the expression immediately, before other arguments have even been added. Changing the first item name in game data method to currentScreen BranchMoney doesn't seem to make a difference.
I've also tried adding a return in the game data method, like this: blacksmith action add: [^blacksmith BranchMoney], so that it would have a value to return, no luck. Doing something like currentScreen := [^currentScreen action at: 1] in the action method doesn't work either.
For some shots in the dark, I tried the ExternalProcedure call and call: methods, but that failed too.
In Smalltalk every block is a regular object that you can store and retrieve the same you would do with any other object:
b := [self doSomething]
stores in b the block (much as b := 'Hello' stores a string in b). What I think you are missing is the #value message. To execute the block do the following
b value "execute self doSomething and answer with the result"
In case your block has one argument use #value: instead
b := [:arg | self doSomethingWith: arg]
later on
b value: 17 "execute the block passing 17 as the argument"
(for two arguments use #value:value:, for three #value:value:value: and for many #valueWithArguments:.)
Note however that this approach of using blocks and Arrays of arguments doesn't look very elegant (or even convenient). However, to help you with some better alternative we would need to learn more about your game. So, go check whether #value (and friends) let you progress a little bit and feel free to come back here with your next question. After some few iterations we could guide you towards a clearer route.
Example
b := [:m | m < 20 ifTrue: ['bad'] ifFalse: ['good']].
will produce
b value: 15 "==> 'bad'"
b value: 25 "==> 'good'"

Can one create a standalone method/function (without any class)

I am trying to understand smalltalk. Is it possible to have a standalone method/function, which is not part of any particular class, and which can be called later:
amethod ['amethod called' printNl].
amethod.
Above code gives following error:
simpleclass.st:1: expected Eval, Namespace or class definition
How can I use Eval or Namespace as being suggested by error message?
I tried following but none work:
Eval amethod [...
amethod Eval [...
Eval amethod Eval[... "!"
Eval [... works but I want to give a name to the block so that I can call it later.
Following also works but gets executed immediately and does not execute when called later.
Namespace current: amethod ['amethod called' printNl].
Thanks for your insight.
In Smalltalk the equivalent to a standalone method is a Block (a.k.a. BlockClosure). You create them by enclosing Smalltalk expressions between square brackets. For example
[3 + 4]
To evaluate a block, you send it the message value:
[3 + 4] value
which will answer with 7.
Blocks may also have arguments:
[:s | 3 + s]
you evaluate them with value:
[:s | 3 + s] value: 4 "answers with 7"
If the block has several sentences, you separate them with a dot, as you would do in the body of a method.
Addendum
Blocks in Smalltalk are first class objects. In particular, one can reference them with variables, the same one does with any other objects:
three := 3.
threePlus := [:s | three + s].
for later use
threePlus value: 4 "7"
Blocks can be nested:
random := Random new.
compare := [:p :u | u <= p]
bernoulli60 := [compare value: 0.6 value: random next].
Then the sequence:
bernoulli60 value. "true"
bernoulli60 value. "false"
...
bernoulli60 value. "true"
will answer with true about 60% of the times.
Leandro's answer, altough being correct and with deep smalltalk understanding, is answering what you asked for, but I think, not 100% sure thou, you are actually asking how to "play" around with a code without the need to create a class.
In my eyes want you want is called a Workspace (Smalltalk/X and Dolphin) (it can have different names like Playground in Pharo Smalltalk).
If you want to play around you need to create a local variable.
| result |
result := 0. "Init otherwise nil"
"Adding results of a simple integer factorial"
1 to: 10 do: [ :integer |
result := result + integer factorial
].
Transcript show: result.
Explanation:
I'm using a do: block for 1-10 iterration. (:integer is a block local variable). Next I'm, showing the result on Transcript.

Is there really no predefined dynamic 2d container in Smalltalk? Do I have to make my own?

I need a dynamic 2d container and I was suprised that I could not find anything usefull in the Collections. So I made my own in oldskool fashion but somehow I feel like there must be somthing im missing. The whole concept in smalltalk pharo is based on using their stuff instead of having to build your own.
OK, so you want to have a collection of objects (morphs in your case) arranged by rows and columns. Here is one way to do this
Initialization: Create an instance variable in your class for holding the objects, and initialize it as:
morphs := OrderedCollection new
Addition: Place new objects in your collection by means of a method like this one
placeMorph: morph atRow: i column: j
| row |
row := morphs at: i ifAbsentPut: [OrderedCollection new].
j - row size timesRepeat: [row add: nil].
row at: j put: morph
Note that by adding nil exactly j - row size times (which could be <= 0) ensures the existence of a slot at row i column j.
Retrieval: Get the object at a given position in the grid or nil
morphAtRow: i column: j
| row |
row := morphs at: i ifAbsent: [^nil].
^row at: j ifAbsent: [nil]
Another possibility would be to use a Dictionary, which could make sense if the grid is large and sparse. In that case you could do the following
Initialization
morphs := Dictionary new
Addition
placeMorph: morph atRow: i column: j
morphs at: i -> j put: morph
Retrieval
morphAtRow: i column: j
^morphs at: i -> j ifAbsent: [nil]
Note that I've used associations i -> j for the keys. Another possibility would have been to use pairs {i.j}.
Pharo has Matrix class. That's pretty much a 2d container, unless you are talking about something else I do not understand :)

For loop for array in Pharo Smalltalk

I'm trying to make an array with random numbers (just 0 or 1), but when I run it, it just prints this: End of statement list encountered ->
This is my code:
GenList
| lista |
lista := Array new: 31.
1 to: 30 do: [ :i | lista at: i put: 2 atRandom - 1]
^lista
What can I do?
Some interesting things to consider:
1. The method selector doesn't start with a lowercase letter
It is a tradition for selectors to start with a lowercase letter. In this sense, genLista would be more correct than GenLista.
2. The method selector includes the abbreviated word 'gen'
For instance, genLista could be renamed to genereLista o listaAlAzar (if you decide to use Spanish)
3. The Array named lista has 31 elements, not 30
The result of Array new: 31 is an array of 31 elements. However, the code below it only fills 30 of them, leaving the last one uninitialized (i.e., nil). Possible solution: lista := Array new: 30.
4. A dot is missing causing a compilation error
The code
1 to: 30 do: [ :i | lista at: i put: 2 atRandom - 1]
^lista
does not compile because there is no dot indicating the separation between the two sentences. Note that the error happens at compilation time (i.e., when you save the method) because the return token ^ must start a statement (i.e., it cannot be inlined inside a statement).
There are other cases where a missing dot will not prevent the code from compiling. Instead, an error will happen at runtime. Here is a (typical) example:
1 to: 10 do: [:i | self somethingWith: i] "<- missing dot here"
self somethingElse
the missing dot will generate the runtime error self not understood by block.
5. There is a more expressive way of generating 0s and 1s at random
The calculation 2 atRandom - 1 is ok. However, it forces the reader to mentally do the math. A better way to reveal your intention would have been
#(0 1) atRandom
6. When playing with random numbers don't forget to save the seed
While it is ok to use atRandom, such a practice should only be used with "toy" code. If you are developing a system or a library, the recommended practice is to save the seed somewhere before generating any random data. This will allow you to reproduce the generation of random quantities later on for the sake of debugging or confirmation. (Note however, that this will not suffice for making your program deterministically reproducible because unordered (e.g. hashed) collections could form differently in successive executions.)

Smalltalk Pharo String concatenation instead of streams

why would the following code:
| list types |
list := Heap new.
types := #('a' 'b' 'c').
types do:[ :t |
1 to:9 do:[ :i |
list add:(t, i asString).
].
].
^ list
issue the String concatenation instead of streams warning in a method in Pharo?
Clicking the [?] button shows:
String concatenation instead of streams
Check for code using string concatenation inside some iteration message.
Am I doing something that can be done easier with streams? What I want to achieve is to create a list of all values a1 to a9, b1 to b9 and c1 to c9.
It complains because of the part t, i asString that is inside a collection loop (you can look at the actual implementation of the rule in the class RBStringConcatenationRule.
Normally string concatenation is discouraged because it's slower and more memory intense (IIRC about the memory).
So if you are doing some heavy concatenation (connecting a lots of parts into a single string), stream is preferable: you can look at most printOn: methods in the system to see it in action.
However in trivial cases concatenation with , is just fine, the warning rule is just too broad. Warnings are just that... warnings that something might be wrong, or that something might be written better.
Speaking of better writing, in Smalltalk it is preferable to use specialized collection methods (select:,collect:,...) over the overly-generic do:, e.g.
| list types |
types := #('a' 'b' 'c').
list := types flatCollect: [ :t | (1 to: 9) collect: [ :i | t , i asString ].
^ Heap withAll: list
(and if you don't need Heap you can just return the third line directly and not have the list tempvar.
To mininize the number of created objects, you could do like this:
| list types digits |
list := Heap new.
types := #($a $b $c).
digits := (1 to: 9) collect: #asCharacterDigit.
types do: [ :t |
digits do: [ :d |
list
add: ((String new: 2)
at: 1 put: t;
at: 2 put: d;
yourself)
] ].
^ list
So you are not creating interim strings and intervals. Nor conversions from integer to string.