контрольная оценка в схеме (хитрость)

Это кажется простым вопросом; возможно, это так просто, что трудно найти поиск, который найдет ответ. В Scheme (в частности, в реализации Guile, если это имеет значение), как мне оценить то, что было процитировано?

Вот что я пытаюсь сделать.

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

Похоже, что "eval" предполагается делать то, что я хочу, но у него есть две проблемы:

  1. Его использование не рекомендуется, поэтому я чувствую, что должен быть лучший способ выполнить то, что я хочу сделать здесь.
  2. На схеме видно, что eval принимает второй параметр, который является средой. Это сбивает меня с толку. Я хочу, чтобы он оценивался в той же среде, в которой появляется оператор, так зачем мне нужен второй параметр? Это вообще возможно? Я немного поигрался с eval, и оказалось, что для некоторых реализаций требуются разные параметры (например, mit-scheme даже не знает, что такое (interaction-environment)!!!)

Я пробовал другие трюки, такие как создание лямбда:

(list 'lambda '() '(car (b c)))

но похоже, что это должно быть оценено для создания процедуры. Я также пробовал:

(list lambda '() '(car (b c)))

но это возвращает «примитивный встроенный макрос», который тоже не работает.

Изменить: похоже, что макрос будет работать для управления порядком оценки: (defmacro test1 (a b) `(begin, b, a))


person Michael    schedule 08.07.2011    source источник


Ответы (3)


eval - совершенно неправильный инструмент для простого изменения порядка оценки аргументов. Вместо этого создайте макрос:

;; (my-fun e1 e2)
;; Just calls my-real-fun, but evaluates e2 before e1
(define-syntax my-fun
  (syntax-rules ()
    [(my-fun e1 e2)
     ;; let* has guaranteed order of evaluation
     (let* ([y e2]
            [x e1])
       (my-real-fun x y))]))

(define (my-real-fun x y) ....)

Или используйте defmacro, если необходимо.

person Ryan Culpepper    schedule 09.07.2011

Если вам нужно оценить структуру списка (вложенные списки с символами в кавычках, которые представляют текст программы Scheme), вам следует использовать eval. Схема требует передачи среды в качестве второго аргумента, даже если это текущая среда:

(eval '(+ x y) (interaction-environment))

Если вам просто нужно выполнять вычисления в определенном порядке, вы можете установить порядок оценки побочных эффектов, используя begin, let или просто тело функции. Они определяют последовательность оценок:

(let ((x 42))
  ; eval with effects #1
  (display x)
  ; eval with effects #2
  (display (+ x 1)))

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

  • Макрос (как вы уже упомянули, просто для полноты):

    > (defmacro test1 (a b) `(begin ,b ,a))
    > (test1 (display 2) (display 3)
    32
    
  • Отложенное вычисление (специальный синтаксис схемы для ленивых вычислений):

    > (define (test1 a b) (begin (force b) (force a)))
    > (test1 (delay (display 2)) (delay (display 3)))
    32
    
  • Обычная лямбда-абстракция и приложение

    > (define (test1 a b) (begin (b) (a)))
    > (test1 (lambda () (display 2)) (lambda () (display 3)))
    32
    
person Andrey Vlasovskikh    schedule 09.07.2011
comment
Как работает макрос, если мне нужно переменное количество аргументов? (defmacro test1 (a . b) `(begin ,b ,a)) не работает, потому что b теперь список. Мне нужно как-то вварить его в начало, но разные попытки типа `(begin (if (pair? ,b) (test1 ,b)) ,a)) не работают. - person Michael; 09.07.2011
comment
также я не могу использовать (defmacro test1 (a . b) `(begin (apply begin ,b) ,a)) , потому что не могу применить макрос. - person Michael; 09.07.2011
comment
тот, кого вы пропустили, это (defmacro test1 (a . b) `(begin ,(cons 'begin b) ,a)) - person Dan D.; 09.07.2011

Вы были на правильном пути с передачей лямбда-выражений. Если у вас есть

(define (f x y z) ...)

... тогда вы можете назвать это так:

(f
  (lambda () a)
  (lambda () b)
  (lambda () c))

Это вызовет f со всеми аргументами (a, b, c) в невычисленной форме. Внутри f у вас есть полная власть выбирать порядок их оценки. Единственное отличие состоит в том, что вы должны явно вызывать (x), (y) и (z) и фиксировать их значения внутри операторов, подобных define или let. Это позволяет гарантировать, что побочные эффекты возникнут только один раз.

Здесь вообще не нужны макросы. Кстати, не беспокойтесь об использовании большого количества лямбда-выражений везде, они очень дешевы.

person Yawar    schedule 28.01.2014