Home > database >  Can someone breakdown what's happening in this piece of code?
Can someone breakdown what's happening in this piece of code?

Time:10-10

I'm very new to LISP programming and I'm having a real hard time with the syntax. The following code is from my notes and I know what it does but I'd really appreciate a line by line breakdown to better understand what's happening here. The "when" loop seemed pretty simple to understand but specifically I'm having a hard time trying to understand the first 3 lines in the "do" loop. Also I'm not sure why (:= acc (1 acc) was used in the last line of the when loop.

(defun count-lower-case-vowels (str)
  (do ((i 0 (1  i))
     (acc 0)
     (len (length str)))
     ((= i len) acc)
   (when (or (equal (aref str i) #\a) (equal (aref str i) #\e)
             (equal (aref str i) #\i) (equal (aref str i) #\o)
             (equal (aref str i) #\u))
     (:= acc (1  acc)))))

CodePudding user response:

I'm a big proponent of lots and lots of extra white space, to achieve visual code alignment (in 2D, yes, as if on a piece of paper) to improve readability:

(defun count-lower-case-vowels (str)
  (do (  (i    0      (1  i) )         ; loop var `i`, its init, step exprs
         (acc  0             )         ; loop var `acc`, its init expr
         (len  (length str)  )  )      ; loop var `len`, its init expr
      ((= i len)                       ; loop stop condition
         acc)                          ; return value when loop stops
    (if                                ; loop body: if
        (find  (aref str i)  "aeiou")  ;              test
      (setf  acc  (1  acc)))))         ;              consequent

Is this better?

It is definitely not the accepted standard of LISP code formatting. But whatever makes it more readable, I think is for the best.

The i's step expression's meaning is that on each step after the loop didn't stop and its body was evaluated, (setf i (1 i)) is called. acc and len have no step expressions, so for them nothing is called on each step.

As to the "when loop" you mention, it is not a loop at all, and is not a part at all of the do loop's looping mechanism. A when form is just like an if without the alternative, which also allows for multiple statements in the consequent, as if with an implicit progn:

(when test a1 a2 ...)
===
(if test (progn a1 a2 ...))

It just so happens that this loop's body consists of one form which is a when form. I have re-written it with an equivalent if.

CodePudding user response:

do is a macro expecting 3 parameters:

(do ((i 0 (1  i))
     (acc 0)
     (len (length str))) ;; first argument
    ((= i len) acc) ;; Second one
  (when ...) ;; third
)
  • The first argument is itself a list, each element of this element being of the following form:

<var-name> <var-initial-value> <var-next-value>

In your case, the form (i 0 (1 i)) means that in the body of the do macro (= in the third argument), you introduce a new, local variable called i. It starts with the value 0, and at each step of the loop, it gets updated to the value (1 i) (i.e. it gets incremented by 1).

You see that the second element of this list is acc 0 with no <var-next-value> in it. It means that acc won't get updated automatically by the macro, and its value will change only according to what is done in the body.

  • The second argument is a list of one or (optionally) two elements <condition> <return-val> The first one <condition> is stating when to stop the iteration: once it evaluates to true, the macro stops. It gets evaluated before each iteration. The second, optional part, is a form stating what the do form returns. By default, it returns nil, but if you specify a form there, it will be evaluated before exiting the loop and return-val is returned instead.

  • The third argument is simply a list of forms that will get executed at each step, provided the condition is false.

  • Related