Схема раннего «возврата при коротком замыкании»?

StackOverflow https://stackoverflow.com/questions/2434294

Вопрос

Я пытаюсь выяснить, как я могу сделать «ранний возврат» в процедуре схемы. без с использованием верхнего уровня if или cond как конструкция.

(define (win b)
 (let* ((test (first (first b)))
        (result (every (lambda (i) (= (list-ref (list-ref b i) i) test))
                       (enumerate (length b)))))
  (when (and (not (= test 0)) result) test))
 0)

Например, в приведенном выше коде я хочу win вернуться test если when условие выполнено, в противном случае верните 0.Однако происходит следующее: процедура всегда вернуть 0, независимо от результата when состояние.

Причина, по которой я структурирую свой код таким образом, заключается в том, что в этой процедуре мне нужно выполнить множество сложных проверок (несколько блоков, подобных let* в примере) и сложив все в большой cond было бы очень громоздко.

Это было полезно?

Решение

Вот как использовать call/cc для сборки return сам.

(define (example x)
  (call/cc (lambda (return)
    (when (< x 0) (return #f))
    ; more code, including possible more calls to return
    0)))

Некоторые схемы определяют макрос под названием let/cc, который позволяет вам убрать часть шума лямбды:

(define (example x)
  (let/cc return
    (when (< x 0) (return #f))
    0))

Конечно, если ваша схема этого не делает, написать let/cc тривиально.


Это работает, потому что call/cc сохраняет точку, в которой он был вызван, как продолжение.Он передает это продолжение аргументу своей функции.Когда функция вызывает это продолжение, Scheme отказывается от всего стека вызовов, который она создала на данный момент, и продолжает работу с конца вызова call/cc.Конечно, если функция никогда не вызывает продолжение, она просто возвращается в обычном режиме.

Продолжения не станут по-настоящему запутанными, пока вы не начнете возвращать их из этой функции или, возможно, не сохраните их в глобальной структуре данных и не вызовете их позже.В остальном они аналогичны операторам структурированного перехода любого другого языка ( while/for/break/return/continue/Exceptions/conditions).


Я не знаю, как выглядит ваш полный код, но, возможно, лучше использовать условие и выделить сложные проверки в отдельные функции.нуждающийся return и let* обычно является признаком чрезмерно императивного кода.Однако метод call/cc должен заставить ваш код работать на данный момент.

Другие советы

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

Вы можете использовать поддержку «вызова с текущим продолжением» для имитации возврата.Есть пример на Википедия.Функция называется вызов с текущим продолжением, хотя часто существует псевдоним под названием звонок/копия что это точно то же самое.Есть также немного более понятный пример здесь

Примечание:Это довольно продвинутая техника программирования на Scheme, и поначалу она может показаться немного сложной...!!!!

В этом случае вам не нужно «когда», вам нужно «если», хотя и не верхнего уровня.

(define (win b)
  (let* ((test (first (first b)))
         (result (every (lambda (i) (= (list-ref (list-ref b i) i) test))
                        (enumerate (length b)))))
    (if (and (not (= test 0)) result) 
        test
        0)))

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

(define foo 
  (lambda (b)
     (begin
       (let ...)
       0)))

и метод Begin заключается в том, что он возвращает результат последней формы внутри, отбрасывая при этом все промежуточные результаты на пол.Эти промежуточные результаты имеют побочные эффекты.Вы не используете ничего из этого, и это здорово (!), но вы должны быть осторожны, чтобы внутри определения функции была только одна форма (результат которой вам действительно нужен).

Грем

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top