2011-01-03 4 views
4

Я пишу небольшой интерпретатор для C-подобного языка на схеме (R5RS) и пытаюсь преобразовать что-то вроде:для/продолжить в схеме/сюсюкать

for (i = 0; i < 100; i++) 
{ 
    if (isprime(i)) continue; 
    else /* do something with i */ 
} 

актуального Scheme (функция IsPrime является просто пример и не важно).

Однако после некоторого времени я не смог найти эффективный/простой способ добавить эквивалент оператора continue в цикл do в Схеме. Что еще лучше было бы для макроса «для», который позволяет использовать «продолжить» и «разбить».

Я рассматриваю возможность перехода на Common Lisp. Будет ли это проще в CL?

ответ

3

Мы можем писать FOR как макрос. Общая версия Lisp:

(defmacro for ((var start end) &body body) 
    (let ((block-name (gensym "BLOCK"))) 
    `(loop for ,var from ,start below ,end 
      do (block ,block-name 
       (flet ((continue() 
         (return-from ,block-name))) 
        ,@body))))) 


CL-USER 2 > (for (i 10 20) 
       (if (evenp i) (continue)) 
       (print i)) 

11 
13 
15 
17 
19 
1

Я бы выбрал continuations, как в этом примере псевдосхемы.

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

(call/cc (lambda break ; jump outside the for 
    (for 0 100 (lambda i 
    (call/cc (lambda continue ; jump to the next iteration 
     (if (isprime i) 
     (continue) 
     (break)))))))) 
+0

Я предположил, что это будет вызов/cc (который отлично работает для разрыва), но почему это выполняет приращение, когда вы продолжаете? Кроме того, я предполагаю, что это довольно неэффективно, так как оно обязательно должно быть продолжено на каждой итерации цикла. – Bill

+1

'Continue' в основном просто пропускает остальную часть тела цикла, что и делает моя конструкция. Скачок не мешает самому циклу, поэтому приращение произойдет потом. У меня нет данных об эффективности, хотя ваше предположение может быть правильным. Просто попробуйте. – Dario

+0

Ах, конечно. Ты прав! – Bill

1

CL-х tagbody является удобной мишенью:

(let (i) 
    (tagbody 
    (setf i 0) 
    body 
    (if (isprime i) 
     (go increment)) 
    (do-something-with i) 
    increment 
    (setf i (1+ i)) 
    (if (< i 100) 
     (go body)))) 
+0

Ах, Lisp выглядит так, будто у него есть какой-то потенциал! – Bill

0

Для реализации этого конкретного примера кода в схеме, вам не нужно continue, break или call/cc:

(let loop ((i 0)) 
    (when (< i 100) 
     (if (prime? i) 
      (loop (add1 i))) 
     (do-something-else))) 
+0

К сожалению, я не могу найти форму «когда» в стандарте R5RS. Предполагая, что он делает то, что я думаю, работает ли этот код. Предположим, что это продолжается, вызывая цикл. Что происходит, когда он заканчивает вызов цикла? Затем оно продолжается и завершает тело, т. Е. Выполняет еще одно (делать что-то еще)? Разумеется, это решение работает только в том случае, если (loop (add1 i)) является хвостом вызова? Или я не понимаю, как работает named let? – Bill

+0

Да, конечно, вы можете найти обходное решение в большинстве * особых случаев (что я даже считаю предпочтительным в функциональном стиле). OP, хотя и запрашивает решение * general *, где 'break' /' continue' уже используются. – Dario

+0

На самом деле, это действительно приводит к решению, я думаю. См. Пример ниже. – Bill

0

Я думаю, Ответ Vijay может быть расширен таким образом, который работает (извините за ответ на мой собственный вопрос, но не могу понять, как форматировать код в комментарии):

(let loop ((i 0)) 
    (define (next) 
     (loop (+ i 1))) 
    (call/cc 
     (lambda (break) 
     (if (< i 100) 
     (begin 
      (if (isprime i) 
      (next) 
      (begin 
       (if (isbad i) 
       (break break)) 
       (do-something) 
       (next)))))))) 

Это не макро, но, несомненно, приводит к тому, который это достаточно общее. Мне было бы интересно увидеть какие-либо улучшения. Я довольно новичок в Scheme.

Смежные вопросы