Reputation: 1784
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
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
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
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
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