Прежде всего, это будет зависеть от вашей реализации, поскольку вложенные определения могут быть реализованы несколькими способами.На моей установке Chez Scheme 9.5 я получаю довольно стабильное время выполнения на 25%, когда я использую odd-internal.
Теперь, почему.Это происходит потому, что вложенные определения (то есть внутренние определения) сильно отличаются от фактических определений.
Когда вы используете define
на верхнем уровне, вы добавляете новую запись в таблицу свободных переменных.Всякий раз, когда вы пытаетесь оценить переменную, которая не привязана ни к одной лямбде, она ищется в таблице свободных переменных (хэш).Этот поиск очень эффективен, но он медленнее, чем выбор связанной переменной.Поэтому, когда вы вычисляете (odd-external 40000000)
, что вы извлекаете even
и odd-external
из этой таблицы около 40 миллионов раз - даже с кэшированием и другими классными вещами, это все еще работа, которую нужно сделать.
Напротив, вложеннаяопределяет создать связанную переменную.Один из способов их реализации - это вложенные лямбда-выражения / let / letrec.Таким образом функция odd-internal
будет преобразована в [1] :
(define (odd-internal x)
(let ((even (lambda (x)
(if (zero? x)
#t
(odd-internal (sub1 x))))))
(if (zero? x)
#f
(even (sub1 x)))))
(что является упрощением того, что делает схема Chez).
Теперь каждый раз, когда вы применяетеodd-internal
это все еще свободная переменная, так что вы хешируете ее и находите ее в таблице свободных переменных.Однако, когда вы применяете even
, вы просто извлекаете его из среды (что может стоить всего лишь разыменования памяти даже без хитрых уловок).
Интересным экспериментом будет определение обоих odd
и even
как связанные переменные, поэтому все выборки с переменными 40 милов выиграют от быстрого времени получения связанных переменных.Я увидел улучшение на 16% по сравнению с исходными 25%.Вот код:
(define (odd-quick x)
(define (odd x) (if (zero? x) #f (even (sub1 x))))
(define (even x) (if (zero? x) #t (odd (sub1 x))))
(odd x))
[1] let
- синтаксический suger для приложения lambda
, поэтому вы можете прочитать, чтокод как:
(define (odd-internal x)
((lambda (even)
(if (zero? x)
#f
(even (sub1 x))))
(lambda (x)
(if (zero? x)
#t
(odd-internal (sub1 x))))))