Функции с нефиксированным числом аргументов
Поможем в ✍️ написании учебной работы
Поможем с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой

Дляdefun, lambda - или nlambda, если в качестве списка формальных параметров указана переменная без скобок, то данная функция (именованная или безымянная) имеет нефиксированное число аргументов.

Определим функцию append2, которая отличается от функции append1тем, что может строить конкатенации нескольких списков:

(defun append2 L (if (null L) nil

(append1 (car L) (apply 'append2 (cdr L))))

)

К сожалению, рекурсивный вызов (append2 (cdr L))не проходит. Причина в том, что при пустом (cdr L)вызов имеет вид (append2 nil),а в этом случае аргумент воспринмается, как (nil),т.е. непустой список с одним пустым элементом. В отличии от вызова (append2) при (append2 nil)выход из рекурсии невозможен.

2.7.5. Фунарг-проблема. Замыкание

С использованием функционалов и функций с функциональным значением связана так называемая фунарг-проблема. Мы рассмотрим эту проблему сначала на примере.

Ранее мы уже определили с помощью применяющего функционала функционал mapcar (упрощенный вариант) в следующем виде:

(defun mapcar1 (fn L) (cond ((null L) nil)

(t (cons (funcall fn (car L))(mapcar1 fn (cdr L))))))

Используем это определение в следующей функции proba:

(defun proba (L) (mapcar1 '(lambda (x) (cons x L)) '(a b c)))

У переменной L есть свободное вхождение в лямбда-выражение, являющееся функциональным аргументом в функции proba, и связанные вхождения в функциях proba и mapcar1. Не очевидно, какое значение L представляет каждое вхождение. Если мы вызовем, например. функ цию proba с аргументом nil, то статическим значением L функции proba будет nil. Вызывая функцию mapcar1, мы получим следующие значения для ее функционального аргумента FN и списочного аргумента L:

FN = (lambda (x) (cons x L))

L =(А В С)

Суть проблемы связана со свободной переменной L из лямбда-выражения, являющегося значением параметра FN. Определяется ли ее значение в момент применения лямбда-выражения статически в соответствии со значением параметра L =nil функции proba либо динамически в соответствии со значением параметра L функции mapcar1? В последнем случае значениями L на различных уровнях рекурсии были бы список (А В С) и его последовательные хвосты.

Эти интерпретации дали бы два различных ответа:

1.((А) (В) (С)) - статическая интерпретация

2.((А А В С) (В В С) (С С)) - динамическая интерпретация

Различные результаты получаются из-за того, что контекст, в котором вычисляется функциональный аргумент, зависит от контекста момента вызова, и он может меняться от вызова к вызову. Проблемы могут возникнуть, когда в контекстах на момент определения и на момент вызова используются одинаковые имена, для переменных в действительности представляющих различные объекты. Значения свободных переменных функционального аргумента могут неожиданно измениться на некотором этапе вычислений. Чтобы избежать конфликта имен и возникновения этой проблемы, программист должен был бы при применении функционала знать имена его внутренних переменных.

Рассмотренную выше проблему определения контекста при вычислении свободных переменных функционального аргумента в Лиспе называют фунарг-проблемой. Эта проблема получает разрешение путем использования в качестве функционального аргумента замыкания, в котором можно зафиксировать значения свободных переменных из контекста момента определения. При этом конфликты по именам будут исключены.

Например, если вместо quote указанное выше лямбда-выражение будет передано как замыкание с помощью формы function, то в функции proba значение переменной L из функционального аргумента mapcar1 будет всегда совпадать со значением статической переменной l функции proba:

(defun proba (L) (mapcarl (function (lambda (x) (cons x L))) '(a b c)))

proba

(proba nil)

((А) (В) (C))

До того как фунарг-проблема прояснилась до конца, она успела создать много неприятностей как создателям Лисп-систем, так и программистам. Сначала казалось, что это - проявление ошибки в интерпретаторе Лиспа. После того как никакой ошибки не было обнаружено и положение так и оставалось неясным, в различных реализациях стали ограничивать использо вание функционалов, не рекомендуя использовать их в программах. В основе возникших проблем лежало то обстоятельство, что теоретические положения лямбда-исчисления не были в достаточной степени учтены при реализации языка: в самом языке Лисп или в формализме лямбда-исчисления никаких ошибок нет.

Вся совокупность вопросов получила окончательное разрешение после того, как Лэндин (Landin [22]) формализовал программные аспекты лямбда-исчисления, специально учитывая факторы, связанные с функционалами и контекстом вычислений. Для решения проблемы контекста он предложил понятие замыкания, которое с тех пор занимает важное место в программировании, особенно в управлении параллельными процессами.

Cуществуют два вида замыканий в зависимости от того, имеем ли мы дело с функциональным аргументом, передаваемым функционалу (downward funarg), или с возвращенным в качестве результата вычисления функциональным объектом (upward funarg). Первоначально фунарг-проблема осознавалась только для первого случая. Полученное для этого случая решение оказалось в какой-то мере применимо и ко второму случаю, и таким образом вся проблема получила свое решение.

Фунарг-проблема особенно неприятна для тех систем, в которых интерпретатор использует динамические вычисления, а оттранслированные программы - статические. В них, использующие функциональный аргумент свободные переменные работают по-разному в интерпретируемом и транслированном варианте.

Не все Лисп-системы допускают использование замыканий.

Дата: 2016-10-02, просмотров: 243.