squirl
squirl

Reputation: 1784

Racket built-in repetition functions

I was looking for a way to repeat a function call a certain number of times and accumulate the results in a list and couldn't find anything in the standard library, so I wrote one. It's simple to write, but it seems like such an obvious thing that I feel like there must be an accepted way of doing this using standard library functions.

Here are the two functions I'm looking to replace:

(define (repeat n f)
  (unless (<= n 0)
    (f)
    (repeat (sub1 n) f)))

(define (accumulate n f)
  (let loop ([n n] [l empty])
    (if (<= n 0)
        l
        (loop (sub1 n)
              (cons (f) l)))))

Is there any simpler way of achieving this?

Upvotes: 1

Views: 1826

Answers (4)

Alex Gian
Alex Gian

Reputation: 514

OK, very old question, but I can't help thinking that what the OP wanted was a running reduction, which I don't think has been given yet. IOW

(define (r-reduction op init n) ; plain vanilla recursive
  (if (zero? n) '()
      (let ((elem (op init)))
        (cons elem  (r-reduction op elem (sub1 n))))))

Now (r-reduction sin 1 5) will give

'(0.8414709848078965 0.7456241416655579 0.6784304773607402 0.6275718320491591 0.5871809965734309) which is the sin accumulated 5 times. If you do it for cos you will get the well known fixed-point convergence.

You can also write the above in a tail-recursive form, or for a function of two arguments, in which case instead of giving a number, n, you can give a list to be reduced:

(define (reductions op init arglist)
  (define (red_ init arglist acc)
    (if (empty? arglist) acc
        (let ((elem (op init (car arglist))))
          (red_ elem (cdr arglist) (cons elem acc)))))
  (reverse (red_ init arglist '())))

For example (reductions * 1 '(1 2 3 4 5 6 7)) will give you the factorials '(1 2 6 24 120 720 5040).

I haven't found functions like these in the standard library, maybe I've just missed them, but I find them quite useful.

Upvotes: 0

Electric Coffee
Electric Coffee

Reputation: 12144

Just throwing in my own two cents here.

Use either foldl or foldr if you want to include the result in each repetition.

;; Just a demo function to showcase the repeater. It moves the first item to the back of the list, like a rotating bit shift
(define (rotate-list lst)
  (append (rest lst) (list (first lst))))

(define (repeat fn times default)
  (let ((rang (range times)))
    (foldl (lambda (_ res) (fn res)) default rang)))

(repeat rotate-list 5 '(the quick brown fox jumps over the lazy dog))
; result: '(jumps over the lazy dog the quick brown fox)

Or, if you want repeat to instead return a function, so it instead creates a new function that applies the other one n times:

(define (repeat fn times)
  (let ((rang (range times)))
    (lambda (default) (foldl (lambda (_ res) (fn res)) default rang))))

(define rotate-five-times (repeat rotate-list 5))

(rotate-five-times '(the quick brown fox jumps over the lazy dog))
; result: '(jumps over the lazy dog the quick brown fox)

Upvotes: 0

John Clements
John Clements

Reputation: 17233

It looks like @AlexKnauth didn't feel like taking your internet points for his answer, and he phrased it as a comment. I'm not proud, though... Use racket's list comprehension form:

(for/list ([i (in-range n)]) (f i))

(I added an explicit "in-range", just to get better error-checking.)

Upvotes: 3

Julien Rous&#233;
Julien Rous&#233;

Reputation: 1113

If your function does not any take arguments, you can use build-list

Example:

#lang racket

;; The function you want to call many times
(define (f)
  #t)

;; Use build list with a lambda to wrap your function because
;; build-list want a function takin integer as its second argument
(build-list 5 (lambda (x) (f)))

result:

'(#t #t #t #t #t)

Edit: you can also define a function to wrap the lambda

(define (repeat-then-accumulate n f)
  (build-list n (lambda (x) (f)))
  )

Usage:

;; Using the f function defined earlier
(repeat-then-accumulate 10 f)

result:

'(#t #t #t #t #t #t #t #t #t #t) 

Edit2: If you want to have fixed args, you could do something like

#lang racket

;; The function you want to call many times
(define (f a b)
  (+ a b))


(define (repeat-then-accumulate n f args)
  (build-list n (lambda (x) (apply f args)))
  )

Usage:

(repeat-then-accumulate 10 f '(3 5))

Result:

'(8 8 8 8 8 8 8 8 8 8)

Upvotes: 3

Related Questions