'whileTrue in a block closure Smalltalk?

Can you use a whileTrue: inside a block closure? I can't get it to work so I suspect not. Or I am doing something wrong.

map := #([1 1 1 1 1]
    [1 0 0 0 1]
    [1 0 1 0 1]
    [1 0 0 0 1]
    [1 1 1 1 1]).

posx := 1.
posy := 1.
t4 := CompositePart new.
rot := (Core.Double.Pi / 4).
perspective := Core.OrderedCollection new.
1 to: 60 do: [:i | perspective add: (rot + (i - 30) asRAD).
            rot_i := perspective at: i.
            x := posx.
            y := posy.
            gsin := (0.2 * (rot_i sin)).
            gcos := (0.2 * (rot_i cos)).
            t2 := true.
            [t2 = true]
                    whileTrue: [
                    x := (x + gcos).
                    y := (y + gsin).
                    n := (n + 1).
                    mapCoord := map at: x rounded asInteger.
                    mapCoord := (mapCoord at: y rounded asInteger).
                    (mapCoord = 1)
                        ifTrue: [
                            h := 1/(0.2 * n).
                            t2 := false]].
            t3 := (i @ h  extent: 2 @ h ) asFiller.
            t4 add: t3].

I would like to get everything inside the whileTrue block to repeat if mapCoord isn't equal to 1. But it doesnt, it just chugs along to the next iteration in the "do:" block. I suspect Im doing something wrong, but I can't figure it out.



Solution 1:[1]

This is what I see.

At every do: iteration, both x and y are initialized to 1 and then, inside the whileTrue:, to something between 0.8 and 1.2. Thus, the first assignment of mapCoord evaluates to #[1 1 1 1 1] and the second to 1 because #[1 1 1 1 1] at: 1 is 1. Thus, t2 becomes false and the whileTrue: exits.

More clearly

x := posx.                                             "x = 1"
y := posy.                                             "y = 1"
gsin := (0.2 * (rot_i sin)).
gcos := (0.2 * (rot_i cos)).
t2 := true.
[t2 = true]
   whileTrue: [
      x := (x + gcos).                                 "0.8 <= x <= 1.2"
      y := (y + gsin).                                 "0.8 <= y <= 1.2"
      n := (n + 1).
      mapCoord := map at: x rounded asInteger.         "mapCoord = #[1 1 1 1 1]
      mapCoord := (mapCoord at: y rounded asInteger).  "mapCoord = 1"
      (mapCoord = 1)
         ifTrue: [
            h := 1/(0.2 * n).
            t2 := false]].                             "t2 = false"

Solution 2:[2]

Syntactically there's no reason that it shouldn't work and as you can run the code, the answer to your question is: Yes you can! If you think the while loop doesn't do what you expect, i suggest you don't "do it", but rather try the "debug it" command. That'll allow you to step through your code statement by statement. You can inspect all variables along the way and maybe find out what's wrong.

Some remarks about your code:

  • you use x/y separately but you're actually talking about a Point, which has a x and y property. It also supports large parts of the number's protocol, which means you can send the message rounded to a Point, too. You can create Point objects via @ like: point := 4 @ 5. Points also allow arithmetics like point * 4 or point + something.
  • rounded returns an Integer, so there's no need to explicitly convert it via asInteger.
  • you have a method asRAD but there's already a system method degreesToRadians
  • you shouldn't number your variables. The decompiler does that because it doesn't know better, but if you don't know how to name your variables, at least name them after the type they have. t4 could be aComposite

Solution 3:[3]

[:i| | eye x y gsin gcos coordinate notLessThanOne h |
" So I can see it too. Yes--A while loop inside a block is fine "
eye := perspective add: (rot + (i - 30) asRAD).
X := posx.                                     " x = 1 "
y := posy.                                     " y = 1 "
gsin := (0.2 * (eye sin)).
gcos := (0.2 * (eye cos)).
coordinate := notLessThanOne.
[ coordinate < 1                              " op changed to < "
] whileFalse: 
      [x := (x + gcos).                       " 0.8 <= x <= 1.2 "
       y := (y + gsin).                       " 0.8 <= y <= 1.2 "
       n := (n + 1).                          " grows forever?  "
       coordinate := map        at: x rounded." is #[1 1 1 1 1] "
       coordinate := coordinate at: y rounded." is 1            "
      ].
h := 1/(0.2 * n).
parts add: (1@h extent: 2@h) asFiller
]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Karsten
Solution 3