Временно перезаписать глобально определенную функцию в блоке let block?

Предположим, у меня есть следующие функции:

(define (g x) (f x))
(define (f x) (+ 1 x))

Я хотел бы временно позвонить g с другим f, Например, что-то вроде этого:

(let ((f (lambda (x) (+ 2 x))))
  (g 5))

Я хотел бы, чтобы код выше оценивать до 7, но это не так. Вместо этого он оценивается в 6, так как g вызывает f выходит за рамки let,

Есть ли способ сделать это без переопределения g внутри letи без включения всего тела определения g в let? (На практике, g может быть очень большой, сложной функцией).

4 ответа

Решение

Я нашел способ сделать именно то, что я хотел, хотя у меня есть чувство, что многие люди не сочтут это кошерным:

(define (g x) (f x))

(define (f x) (+ 1 x))

(let ((old-f f))
  (set! f (lambda (x) (+ 2 x)))
  (let ((ans (g 5)))
    (set! f old-f)
    ans))
; -> 7

(g 5) ; -> 6

В ответ на комментарий ниже, я даже не знал, что fluid-let была вещь. Это даже уже работает на MIT-Scheme. Это именно то, что мне было нужно. Если комментатор ниже публикует что-то вроде этого в качестве ответа, будет принят принятый ответ:

(define (g x) (f x))

(define (f x) (+ 1 x))

(fluid-let ((f (lambda (x) (+ x 2))))
  (g 5)) ; -> 7

(g 5) ; -> 6

То, что вы просите, - это динамическое, а не лексическое связывание 'f'. R6RS и R7RS поддерживают это с помощью параметров. Это будет делать то, что вы хотите:

(define f (make-parameter (lambda (x) (+ 1 x))))
(define (g x) ((f) x))

(display (g 5))(newline)

(parameterize ((f (lambda (x) (+ 2 x))))
  (display (g 5))(newline))

Вы можете использовать необязательный параметр в g пройти f от let выражение.

(define (g x . args)
  (if (null? args)
    (f x)
    ((car args) x)))

а также

(let ((f (lambda (x) (+ 2 x))))
  (g 5 f))

Я не уверен, что вы можете, но я ни в коем случае не эксперт Схемы.

Я понимаю, что вы пытаетесь достичь этого без переопределения g внутри let, но как насчет:

(define (h f x) (f x))
(define (g x) (h f x))
(define (f x) (+ 1 x))

(let ((f (lambda (x) (+ 2 x))))
  (h f 5))

Таким образом, вы сохраняете поведение g где это в настоящее время называется. Но если вы хотите временно изменить свое поведение, вы можете позвонить h вместо.

Чуть больше кода для уточнения:

(let ((f (lambda (x) (+ 2 x))))
  (display (g 5)) ; 6
  (newline)
  (h f 5))        ; 7
Другие вопросы по тегам