Функция оценки лямбда не может быть использована для определения новой функции в области видимости

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

(define (pow-iter base exp r)
  (if (= exp 1)
      r
      (pow-iter base (- exp 1) (* base r))))

(define (pow exp)
  (lambda (base)
    (pow-iter base exp base)))

(define cubic (pow 3))

(cubic 2)

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

Однако, если мы поместим это в такую ​​область:

(define (do-cubic x)
  (define (pow-iter base exp r)
    (if (= exp 1)
        r
        (pow-iter base (- exp 1) (* base r))))

  (define (pow exp)
    (lambda (base)
      (pow-iter base exp base)))

  (define cubic (pow 3))

  (cubic x))

(do-cubic 2)

Я получаю ошибку

Pow: не определено; нельзя использовать до инициализации

Почему возникает эта ошибка и есть ли способ ее исправить, не меняя логику программы?

1 ответ

Решение

Эта программа вызывает ту же ошибку:

#lang r5rs
(let ()
  (define (foo x) (lambda (y) (+ 42 x)))
  (define bar (foo 1))
  (bar 2))

Output:
  foo: undefined;
  cannot use before initialization

Причина, по которой вы получаете ошибку, состоит в том, что "внутренние определения" переписываются в letrec В выражении все привязки действуют, пока их начальные значения вычисляются, что позволяет взаимно рекурсивные определения.

(letrec ((foo (lambda (x) (lambda (y) (+ 42 x))))
         (bar (foo 1)))
 (bar 2))

В R5RS выражения инициализации оцениваются в неуказанном порядке. Это означает, что в первом фрагменте выше возможно (define bar (foo 1)) подлежит оценке до (define (foo x) ...), Другими словами, значение foo нужно раньше foo были инициализированы.

В ракетке (#lang racket) внутренние определения использования letrec*-семантика (т.е. выражения инициализации оцениваются в порядке их появления в коде. Таким образом, программа работает без ошибок.

Обратите внимание также, что letrec в #lang racket соответствует чему letrec* делает в "R5RS"-реализации.

Для получения дополнительной информации о letrec против letrec* см. введение http://www.cs.indiana.edu/~dyb/pubs/letrec-reloaded.pdf

Другие вопросы по тегам