Я изучал Схему недавно и сталкивался с функцией, которая определяется следующим образом:
(define remove!
(let ((null? null?)
(cdr cdr)
(eq? eq?))
(lambda ... function that uses null?, cdr, eq? ...)
Что цель связать является пустым? к пустому указателю? или CDR к CDR, когда они создаются в функциях, которые доступны в функциональном определении без блока, которому позволяют?
В простой R5RS Scheme нет системы модулей - только верхний уровень. Более того, менталитет таков, что все может быть изменено, так что вы можете "настраивать" язык как угодно. Но без системы модулей это работает плохо. Например, я пишу
(define (sub1 x) (- x 1))
в библиотеке, которую вы загружаете - и теперь вы можете переопределить -
:
(define - +) ; either this
(set! - +) ; or this
и теперь вы непреднамеренно сломали мою библиотеку, которая полагалась на sub1
, уменьшающую свой вход на единицу, и в результате ваши окна поднимаются, когда вы перетаскиваете их вниз, или что угодно.
Единственный способ обойти это, который используется несколькими библиотеками, - "захватить" соответствующее определение функции вычитания, прежде чем кто-то сможет его изменить:
(define sub1 (let ((- -)) (lambda (x) (- x 1))))
Теперь все будет работать "более тонко", поскольку вы не сможете изменить значение моей функции sub1
, изменив -
. (За исключением... если вы измените ее до загрузки моей библиотеки... )
В любом случае, в результате этого (и если вы знаете, что -
является оригиналом при загрузке библиотеки), некоторые компиляторы обнаружат это и увидят, что вызов -
всегда будет фактической функцией вычитания, и поэтому они будут инлайнить вызовы к ней (а инлайнинг вызова -
может в конечном итоге привести к ассемблерному коду для вычитания двух чисел, так что это большой прирост скорости). Но, как я уже сказал в комментарии выше, это больше совпадает с фактической причиной, указанной выше.
Наконец, в R6RS (и нескольких реализациях схемы до этого) это исправлено и добавлена библиотечная система, так что этот трюк не используется: код sub1
безопасен до тех пор, пока другой код в его библиотеке не переопределяет -
каким-либо образом, и компилятор может безопасно оптимизировать код на основе этого. Нет необходимости в хитроумных уловках.
Это оптимизация скорости. Доступ к локальным переменным обычно быстрее, чем к глобальным переменным.