2

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.

Aroob
  • 113
  • 8
  • 1
    Lisp is most effective when used as a functional language. You are effectively asking LOOP to SETF a var outside the scope of the loop by manipulating an internal loop var. That should not even be expected to work, once you understand loop. For example, if I am maximizing without "into var" I would expect loop to return that value, but if I am maxing into var I know I have to do an explicit return in a FINALLY clause. btw, if you are doing a bunch of this stuff into several vars just return them as a list or a dedicated struct if you in fact have a struct implicitly in mind. – kennytilton Dec 27 '17 at 10:49
  • @kennytilton Thank for the info. The difference of just maximizing and maximizing into a variable are news. With that in mind, I can see why I should not expect the "maximizing into other var" to work. As to the first sentence "Lisp is most effective ...", then why do we have `loop` and not tail calls :). – Aroob Dec 28 '17 at 23:35
  • 1
    "why do we have loop and not tail calls"? Astute question! Another great thing about CL in particular is that it is not opinionated. Put another way, it is a big ball of mud. The CL developer is not hobbled by purist concerns -- but then takes on the burden of knowing when to break the rules! LOOP wins because it is a DSL for iteration, delivering extraordinary bang for the character count buck. Once one knows LOOP. But hey, it is a DSL, a little extra study is more than justified by having it in one's toolkit. So use it, but (full circle) do not lose the overarching functional inclination. – kennytilton Dec 29 '17 at 11:09

1 Answers1

8

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...

sds
  • 58,617
  • 29
  • 161
  • 278
  • Thanks. Ok, noted. But what about the case that there is "more going" on in the `loop`, i.e. the `maximize` clause is only one of the clauses used. Then I probably would have to maximize manually into the `let` variable? – Aroob Dec 26 '17 at 19:47
  • @Aroob You could also directly do the work you need in the "finally" clause, where all intermediate variables are still in scope (or call another function from there). This makes "loop" a little bit like a "let", where you build a context (min, max, count, intermediate lists, ...) to be used in the "finally" clause. Whether this is more readable or not than returning values depends on the actual use case. – coredump Dec 27 '17 at 21:24
  • 1
    @coredump A real world application the code that makes the values is better off in a function to keep the code that makes and uses separate. Thus `multiple-value-bind` is overkill in the example until you do that. @sds is just a step ahead :) – Sylwester Dec 28 '17 at 19:56
  • 1
    @coredump I guess I wanted to use the max value later on in a function or something like that, without having to have another `let` binding or `multiple-value-bind` or whatever else there is. Kind of, `let` bind at the beginning, do some stuff in the `let` body, and, at the end, return it. – Aroob Dec 28 '17 at 23:39