Search code examples
loopscommon-lisplocal-variableslet-binding

Common Lisp `loop`: maximize into local variable introduced by `let`


The loop facility in Common Lisp allows several value accumulation clauses, maximize amongst others.
Now, it is also possible to give a variable var to the maximize clause:

(loop for x from 0 to 10 maximize (func x) into var)

My question is:

Is it possible to give as var a new local variable introduced by let?

An example scenario would be:

(let ((var -1)) ; assume numeric result
  (loop for x from 0 to 10 maximize (func x) into var))

It is not important that x has a numeric value, it's only for illustration purposes.


Solution

  • Mix bindings?

    No, the into variables are bound by loop.

    What you can do is bind your var to the return value of loop:

    (let ((var (loop for x from 0 to 10 maximize (func x))))
      ;; use var here
      ...)
    

    Complex loop - use multiple values, functional style

    If you are doing many things in a single loop, you might want to use values function in Common Lisp:

    (multiple-value-bind (max min sum)
        (loop for x from 0 to 10
          maximize (f1 x) into max
          minimize (f2 x) into min
          sum (f3 x) into sum
          finally (return (values max min sum)))
      ;; use max, min and sum here
      ...)
    

    Note that the variables max, min and sum bound by multiple-value-bind and loop are completely separate and independent, and have absolutely nothing in common and are named the same for didactic purposes only.

    If you rename them (as you definitely should for the sake of code readability!):

    (multiple-value-bind (max min sum)
        (loop for x from 0 to 10
          maximize (f1 x) into max1
          minimize (f2 x) into min1
          sum (f3 x) into sum1
          finally (return (values max1 min1 sum1)))
      ;; use max, min and sum here
      ...)
    

    and recompile your code, you will see that the disassembly is identical.

    Complex loop, use finally, procedural style

    As suggested by @coredump, you can set your variables in the finally construct:

    ;; bind max, min and sum
    (loop for x from 0 to 10
      maximize (f1 x) into max1
      minimize (f2 x) into min1
      sum (f3 x) into sum1
      finally (setq max max1
                    min min1
                    sum sum1))
    ;; use max, min, and sum; max1 et al do not exist here
    

    Generally, speaking, there is more than one way to skin the cat here...