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

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

(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)

я получаю сообщение об ошибке

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

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


person Community    schedule 20.07.2016    source источник
comment
В Ракете это работает.   -  person Renzo    schedule 20.07.2016
comment
@Renzo Вы имеете в виду, что это работает в #!racket. В #!r5rs даже под RacketVM должен глючить. Это разные языки.   -  person Sylwester    schedule 20.07.2016


Ответы (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.

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

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

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

person soegaard    schedule 20.07.2016