ಠ_ಠ
ಠ_ಠ

Reputation: 3078

What is the correct way to append via loop in racket?

started learning racket today. I was attempting to find the correct way to append via a loop but could not find the answer or figure out the syntax myself.

For example, if I want a row of nine circles using hc-append, how can I do this without manually typing out nine nested hc-append procedures?

Upvotes: 3

Views: 2443

Answers (4)

rnso
rnso

Reputation: 24535

This is an old post but it is highly read, hence I am adding an answer. Following method using 'named let' is also very convenient:

(define (f)
  (let loop ((counter 1)
             (result (blank)))
    (if (< counter 10)
        (loop (add1 counter)
              (hc-append (circle 10) result))
        result)))

Upvotes: 0

Guillaume Marceau
Guillaume Marceau

Reputation: 396

There are three styles of loops available in Racket:

#1 The for-loop style

This style uses the syntax described in Chapter 11 of the Racket Guide, "Iterations and Comprehensions". It looks like this:

(require slideshow/pict)

(for/fold ([result (blank)]) ([i (in-range 9)])
  (hc-append (circle 10) result))

In this style, in the first parenthesis after for defines the loop's temporary variables. There is one such variable in my example, called result. Then you define the iteration variables and what they iterate over. So here, i loops over the numbers for 0 to 8. The body of the loop runs once for each i, each time the result is assigned to result, and the final value of the loop is the value of result at the end.

#2 The map and fold style.

This style is described in Section 3.8 of the Guide. It looks like this:

(require slideshow/pict)
(foldl hc-append (blank) (build-list 9 (lambda (i) (circle 10))))

This code start by making a list of 9 circles:

(define o (circle 10))
(build-list 9 (lambda (i) o)  --->  (list o o o o o o o o o)

The list notation is a shorthand for the more verbose cons notation.

(list o o o o o o o o o)  --->  (cons o (cons o (cons o (cons o (cons o (cons o (cons o (cons o (cons o empty)))))))))

The way to think of the foldl function is that it turns a piece of data into a piece of computation. It takes a list as an input and it turns it into a collection of function calls.

The foldl function takes the list and replaces each cons in the list with the first argument, and replaces the empty at the end of the list with the second argument. In the example, I pass the hc-append function and (blank), so the replacement looks like this:

(foldl hc-append (blank) ...) --->
(hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (blank))))))))))

This sequence of function calls is exactly the one you wanted to avoid writing by hand. Foldl computes it for you.

#3 The recursion style

This is the style Roddy described in his answer. As a general matter of coding style, you should only use the recursion style if there is no simple *for, map, or fold possible, such as when traversing a recursive data structure.

Upvotes: 4

dyoo
dyoo

Reputation: 12013

Roddy of the Frozen Pea has this answer pretty much covered.

I want to add that since this sort of thing is such a common thing to do---to repeat a process and accumulate the result---there are features in the Racket language to express that idea concisely. One of these features is Racket's for/fold loop. As an example:

> (for/fold ([result 'Go!]) 
            ([i (in-range 5)])
    (list 'Duck result))
'(Duck (Duck (Duck (Duck (Duck Go!)))))

Here, we repeat the operation (list 'Duck result) on the initial value 'Go! to accumulate a result. for/fold's implementation is based on the recursive process that Roddy describes, so once you have the fundamentals down, you'll know enough to use for/fold comfortably.

Upvotes: 3

Roddy of the Frozen Peas
Roddy of the Frozen Peas

Reputation: 15185

The first thing you need to realize is that "looping" in Racket is really just recursion. In this case, you want to chain a bunch of drawing calls together. If we wrote this out, our target goal would be this:

(hc-append (circle 10) 
    (hc-append (circle 10)
        (hc-append (circle 10)
            (hc-append (circle 10)
                (hc-append (circle 10)
                    (hc-append (circle 10)
                        (hc-append (circle 10)
                            (hc-append (circle 10)
                                (hc-append (circle 10))))))))))

I am assuming all of our circles are going to be the same radius.

Now, since we're going to be writing a recursive method, we need to think of our base case. We want exactly nine circles drawn. Let's call this maximum number of circles max. Our base case, when we break out of our "loop" will be when we reach max iterations, or when (= iterations max).

Now for the recursion itself. We already know we need to pass in at least two variables, the current iteration iterations, and the maximum iteration max. If you look at the code above, you'll notice that the repeating element in all of the "loops" is the (circle 10). Now there are a number of ways you could pass that along -- some people would choose to just pass the radius for example -- but I think the easiest way would be to pass in the pict of the circle.

Finally, we also have to pass along the picture we've done so far. That is, as we append a circle to our chain, we need to pass this back into the recursive method so we can keep appending.

Now that we've got that squared away, we can define the structure of our recursive method, which we shall call circle-chain-recursive:

(define (circle-chain-recursive iteration max crcle output)
    ; body here
)

The "guts" of our method will be an if. If we've reached the max iteration, return the output. Otherwise append another circle, increment iteration, and call the method again.

(define (circle-chain-recursive iteration max crcle output)
  (if (= iteration max) 
      output
      (circle-chain-recursive
        (+ 1 iteration) max crcle (hc-append crcle output))))

I personally don't like calling recursive looping methods like this directly, so I would write a helper method like this:

(define (circle-chain num radius)
  (circle-chain-recursive 0 num (circle radius) (circle 0)))

Now if I want a series of 9 circles with radius 10, all I have to do is call (circle-chain 9 10).

You'll notice that I passed (circle 0) as in as the parameter called output. This is because the hc-append method requires a pict parameter. Since we're not starting out with any circles, I passed it the equivalent of a "blank" or nil pict. There may be some other way of passing a "blank" pict, but I'm not too familiar with the slideshow/pict libraries to know it.

I hope that clears it up a bit.

Upvotes: 8

Related Questions