> 1 <
Автор | Сообщение |
d_s__
11 сообщений |
#7842 2018-02-04 20:39 GMT+3 часа(ов) |
Непонятна роль funcall при вызове функции, порожденной другой функцией в CL.
1) * (defun foo (x) (+ x 10)) FOO Сначала определяется символ FOO как символ, указывающий на объект-функцию, которая к к аргументу прибавляет 10. 2) * (foo 2) 12 Сначала вычисляется аргумент 2 - так как он самовычисляемый - то получается 2, затем по символу функции вычисляется объект-функция и происходит вызов объекта-функции с аргументом 2. Результат: 12 3) * (funcall 'foo 2) 12 Можно тот же результат получить через funcall, передав в него СИМВОЛ, указывающий на функцию 4) * (defun voo () (lambda (x) (+ x 10))) VOO * (voo) #<FUNCTION (LAMBDA (X) :IN VOO) {AC6C39D}> Создали функцию которая возвращает объект-функцию (а не символ, указывающий на функцию) 5) По идее далее должно работать, но не работает *((VOO) 2) - ошибка! По идее, сначала должна вычислиться 2, потом (voo), которая возвращает объект-функцию - и данных уже достаточно для вызова этой функции с аргументом 2 6) Но работает *(funcall (voo) 4) 14 При это в данном случае мы передаем funcall НЕ СИМВОЛ, а саму объект-функцию. Что-то у меня никак не бьется в голове пункты 3) и 6) Про то, что в CL два пространства имен - в курсе. Но не могу понять как из этого следует необходимость использования funcall - ведь мы объект-функцию подаем уже "на блюдечке с голубой каемочкой". В каком месте я "туплю"? c Scheme все понятнее - там ((VOO) 2) работает нормально. На каком-то этапе вычислений не понятно где лежит объект-функция? |
|
antares0
185 сообщений |
#7843 2018-02-05 06:52 GMT+3 часа(ов) |
В время компиляции (foo) еще не вызвана, окуружение для ее вызова еще не создано, ее результат существует лишь в голове програмиста => компилятор не сможет сделать быстрый маш. код для этого. В процессе стандартизации эту возможность зарезали, разделив на flet времени компиляции и funcall времени исполнения.
Funcall - явное заявление что функцию мы будем искать в время исполнения, а пока компиляция давай посмотрим как сооптимизировать хотя бы аргументы. Ну и традиция лиспа требует что бы список обрабатвался согласно его первому символу ![]() |
|
antares0
185 сообщений |
#7844 2018-02-05 06:58 GMT+3 часа(ов) |
Схемы разрешат это потиворечие либо уходом от компиляции с потерей производительности. либо уходом от "горячего" изменяемого образа в С-шную детерминированость которая замораживает поведение всей програмы.
|
|
skelter
56 сообщений |
#7845 2018-02-05 08:12 GMT+3 часа(ов) |
Цитата Про функцию неточно написано, она не вычисляется (в смысле вычисления выражения). На первом месте выражения обязательно стоит символ. (На самом деле я вру, там может быть лямбда-выражение, но забудем про эту возможность.) Первым делом лисп смотрит, что это за символ. Если символ является не является именем специальной формы или макроса, то лисп считает, что это имя функции, и находит функцию с этим именем. Уже после того, как лисп разберётся с первым элементом списка - именем функции, он вычисляет аргументы и применяет функцию к аргументам. То есть лиспу не нужна в голове функция. Ему нужно имя функции. И отдельное пространство имён функций как раз означает, что отношение «символ является именем функции» ортогонально использованию символов как переменных, меток и т. п. В частности, «символ является именем функции» и «символ является именем переменной, значением которой является функция» - существенно разные вещи. Функцию по имени ищет специальный оператор function, который сокращается до #' . Например, (function foo) и #'foo - это «функция по имени foo» (сама функция, а не имя). Поэтому ((voo) 2) не работает: на первом месте списка стоит не имя функции, а выражение. Синтаксическая ошибка. Это всё касается синтаксиса лиспа как конкретного языка программирования, то есть лиспоспецифические (даже CL-специфические) вопросы. funcall к синтаксису отношения не имеет. Это в первую очередь оператор применения функции к аргументам. Применение функции — тоже операция. В математике её называют «отображение вычисления», но стандартного обозначения у неё нет, а в хаскеле есть - доллар (помимо пробела). В CL это обычная функция, funcall. Поэтому (funcall (voo) 2) прекрасно работает. Когда аргументы funcall-а вычисляются, получается функция и число, и funcall применяет функцию к числу. Если у нас на руках есть функция (скажем, результат вычисления выражения), и мы желаем её к чему-то применить, синтаксис не даёт этой возможности, поэтому приходится явно использовать funcall: (funcall (voo) 4). С этой точки зрения непонятно, почему работает (funcall 'foo 2), ведь его первый аргумент - символ, а не функция. Дело в том, что иногда вместо объекта конкретного типа разрешается использовать «аналогичный» объект другого типа. В лиспе это называется designator: один объект «обозначает собой» другой. Например, часто можно использовать строки вместо имён файлов или символы вместо строк (обычно так делают при определении пакета). Так и funcall, если посмотреть документацию, принимает не функцию, а, более общо, function designator, то есть или функцию, или символ - имя глобальной функции. То есть (funcall 'foo 2) - это (почти) то же, что (funcall #'foo 2). Так же ведут себя некоторые другие стандартные функции: например, mapcar. |
|
d_s__
11 сообщений |
#7846 2018-02-05 11:51 GMT+3 часа(ов) |
Спасибо - стало понятнее.
Все это дает очень интересный эффект: * (defun foo (x) (+ x 10)) FOO * (defvar foo (lambda (x) (+ x 100))) FOO * (foo 4) 14 * (funcall foo 4) 104 * (funcall 'foo 4) 14 * (funcall (function foo) 4) 14 * foo #<FUNCTION (LAMBDA (X)) {AC256FD}> * (function foo) #<FUNCTION FOO> * |
|
> 1 <