`цикл` Common Lisp: максимизировать локальную переменную, введенную `let`

Средство loop в Common Lisp допускает несколько предложений накопления значений, maximize среди прочих.
Теперь также можно указать переменную var в предложении maximize:

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

Мой вопрос:

Можно ли указать в качестве var новую локальную переменную, введенную let?

Примерный сценарий:

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

Неважно, что x имеет числовое значение, это только для иллюстрации.


person Aroob    schedule 26.12.2017    source источник
comment
Лисп наиболее эффективен при использовании в качестве функционального языка. Вы фактически просите LOOP установить SETF переменную за пределами цикла, манипулируя внутренней переменной цикла. Не следует даже ожидать, что это сработает, как только вы поймете цикл. Например, если я максимизирую без var, я ожидаю, что цикл вернет это значение, но если я максимизирую в var, я знаю, что должен сделать явный возврат в предложении FINALLY. Кстати, если вы делаете кучу этих вещей в несколько переменных, просто верните их в виде списка или выделенной структуры, если вы на самом деле неявно имеете в виду структуру.   -  person kennytilton    schedule 27.12.2017
comment
@kennytilton Спасибо за информацию. Разница между просто максимизацией и максимизацией в переменной - это новость. Имея это в виду, я понимаю, почему я не должен ожидать, что максимизация в другую переменную сработает. Что касается первого предложения, то Лисп наиболее эффективен..., тогда почему у нас loop, а не хвостовые вызовы :).   -  person Aroob    schedule 29.12.2017
comment
почему у нас есть цикл, а не хвостовые вызовы? Проницательный вопрос! Еще одна замечательная особенность CL, в частности, заключается в том, что она не самоуверенна. Другими словами, это большой ком грязи. Разработчик CL не стеснен пуристскими заботами, но затем берет на себя бремя знания того, когда следует нарушать правила! LOOP выигрывает, потому что это DSL для итерации, обеспечивающий невероятную отдачу от количества символов. Как только вы знаете LOOP. Но эй, это DSL, небольшое дополнительное изучение более чем оправдано, если оно есть в вашем наборе инструментов. Так что используйте его, но (полный круг) не теряйте всеобъемлющей функциональной направленности.   -  person kennytilton    schedule 29.12.2017


Ответы (1)


Перепутать крепления?

Нет, переменные into связаны loop.

Что вы можете сделать, так это привязать свой var к возвращаемому значению loop:

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

Сложный цикл — использование нескольких значений, функциональный стиль

Если вы делаете много вещей в одном цикле, вы можете использовать функцию values ​​в 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
  ...)

Обратите внимание, что переменные max, min и sum связаны multiple-value-bind и loop являются совершенно отдельными и независимыми, не имеют абсолютно ничего общего и называются то же самое только в дидактических целях.

Если вы переименуете их (что обязательно нужно сделать для удобочитаемости кода!):

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

и перекомпилируйте свой код, вы увидите, что разборка идентична.

Сложный цикл, используйте finally, процедурный стиль

Как предложил @coredump, вы можете установить свои переменные в конструкции finally:

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

Вообще говоря, здесь есть не один способ содрать шкуру с кота...

person sds    schedule 26.12.2017
comment
Спасибо. Ок, отметил. Но как насчет случая, когда в loop происходит нечто большее, т. е. предложение maximize является лишь одним из используемых предложений. Тогда мне, вероятно, придется вручную максимизировать переменную let? - person Aroob; 26.12.2017
comment
@Aroob Вы также можете напрямую выполнить необходимую работу в предложении finally, где все промежуточные переменные все еще находятся в области видимости (или вызвать другую функцию оттуда). Это делает цикл немного похожим на let, где вы создаете контекст (минимум, максимум, количество, промежуточные списки, ...), который будет использоваться в предложении finally. Является ли это более читаемым или нет, чем возвращаемые значения, зависит от фактического варианта использования. - person coredump; 28.12.2017
comment
@coredump В реальном приложении код, который создает значения, лучше разместить в функции, чтобы разделить код, который создает и использует. Таким образом, multiple-value-bind в этом примере является излишним, пока вы этого не сделаете. @sds просто на шаг впереди :) - person Sylwester; 28.12.2017
comment
@coredump Думаю, я хотел позже использовать максимальное значение в функции или что-то в этом роде, без необходимости иметь другую привязку let или multiple-value-bind или что-то еще. Типа, let привязать в начале, сделать что-то в теле let и, в конце, вернуть его. - person Aroob; 29.12.2017