How might I use letrec in this function so that it does the same job as it does with named let?

Viewed 51

I've read here that named let can be rewritten with letrec. And so I proceeded to rewrite the following function with letrec:

(define (duplicate pos lst)
  (let dup ([i 0] [lst lst])
   (cond
     [(= i pos) (cons (car lst) lst)]
     [else (cons (car lst) (dup (+ i 1) (cdr lst)))])))

My attempt at this:

(define (duplicate pos lst)
  (letrec ((dup (lambda ([i 0] [lst lst])
     (cond
       [(= i pos) (cons (car lst) lst)]
       [else (cons (car lst) (dup (+ i 1) (cdr lst)))]))))))

Sadly, when I call it with (duplicate 1 (list "apple" "cheese burger!" "banana")) I get from Racket letrec: bad syntax (missing body). How might I rewrite duplicate with letrec?

2 Answers

As you can see in the documentation for letrec, it has these arguments:

(letrec ([id val-expr] ...) body ...+)

So, you have to add at least one body form after definitions.

I also replaced cond with if (you have only two branches of code), (+ ... 1) with add1 and improved indentation:

#lang racket

(define (duplicate pos lst)
  (letrec ((dup (lambda ([i 0] [lst lst])
                  (if (= i pos)
                      (cons (car lst)
                            lst)
                      (cons (car lst)
                            (dup (add1 i) (cdr lst)))))))
    (dup)))

Test:

> (duplicate 1 (list "apple" "cheese burger!" "banana"))
'("apple" "cheese burger!" "cheese burger!" "banana")

The named let is (more or less) a locally defined regular procedure that is called "behind the scenes".
The body of a named let is not the body of the equivalent "unnamed" let, but the body of that procedure;

(let f ([x init])
   body)

can be rewritten as

(letrec ([f (lambda ([x init]) body)])
    (f))

or, without using default arguments (which some would find clearer),

(letrec ([f (lambda (x) body)])
    (f init))
Related