Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Usage of nested let or let* in scheme

Tags:

scheme

racket

I'm writing a function taking an interval as an argument and returns the percentage error but I stuck in the usage of let or let*. Here's the code:

nested let version:

(define (percent interval)
  (let (start-point (car interval))
    (let (end-point (cdr interval))
      (let (center (/ (+ start-point end-point) 2)))))
  (/ (- end-point center) center))

the error is: let: bad syntax (not an identifier and expression for a binding) in: start-point

let* version:

(define (percent interval)
  (let* ((start-point (car interval))
     (end-point (cdr interval))
     (center (/ (+ start-point end-point) 2))))
  (/ (- end-point center) center))

the error now is:

let*: bad syntax (missing body) in: (let* ((start-point (car interval)) (end-point (cdr interval)) (center (/ (+ start-point end-point) 2))))

I've referred to some documentation but couldn't figure out the mistake as well. (ps: I'm using DrRacket 6.2)

like image 377
Leo Avatar asked Mar 16 '23 00:03

Leo


1 Answers

The final expression, the division itself, needs to go inside the let*. This is because let forms introduce bindings that are only lexically scoped to their bodies. Furthermore, let forms require a body, which is why you're getting that error (in your example, they have no body at all because the final expression is outside them).

To fix this, just move the final expression within the let* form:

(define (percent interval)
  (let* ((start-point (car interval))
         (end-point (cdr interval))
         (center (/ (+ start-point end-point) 2)))
    (/ (- end-point center) center)))

Your version using nested let is slightly wrong in another way: you're missing a level of parens for each binding pair, which is causing the first error. That is, instead of this:

(let (start-point (car interval))
  ...)

...you need this:

(let ((start-point (car interval)))
  ...)

Note the extra parentheses. That said, the let* version is much better! It's just a macro for nested let forms that does the nesting automatically so you don't have to.

One more thing: if you're writing portable Scheme, all the parentheses need to be parentheses. If you're writing Racket, though, you can use square brackets interchangeably with parentheses. Because of this, idiomatic Racket uses square brackets in certain places as a stylistic choice to make the code more readable.

One of these cases is in let binding pairs. Therefore, idiomatic Racket code would look like this:

(define (percent interval)
  (let* ([start-point (car interval)]
         [end-point (cdr interval)]
         [center (/ (+ start-point end-point) 2)])
    (/ (- end-point center) center)))

This just helps to make it more clear that each pair is creating a binding rather than calling a function.

like image 178
Alexis King Avatar answered Apr 01 '23 12:04

Alexis King