for/继续方案/lisp

发布于 2024-10-09 23:52:04 字数 399 浏览 0 评论 0原文

我正在Scheme(R5RS)中为类C语言编写一个小型解释器,并尝试将以下内容转换

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

为有效的Scheme(isprime函数只是一个示例,并不重要)。

然而,经过一段时间的尝试,我一直无法找到一种有效/简单的方法来将 continue 语句的等效项添加到 Scheme 中的 do 循环中。更好的是“for”宏,它允许使用“继续”和“中断”。

我正在考虑转向 Common Lisp。在 CL 中这种事情会更容易吗?

I'm writing a small interpreter for a C-like language in Scheme (R5RS) and trying to convert something like:

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

to valid Scheme (the isprime function is just an example and not important).

However, after trying for some time, I have not been able to find an efficient/simple way to add the equivalent of a continue statement to a do loop in Scheme. What would be even better would be a "for" macro which allows "continue" and "break" to be used.

I'm considering switching to Common Lisp. Would this sort of thing be any easier in CL?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

忘年祭陌 2024-10-16 23:52:04

我们可以将 FOR 写成宏。 Common 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 

We can write FOR as a macro. The Common Lisp version:

(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 
把时间冻结 2024-10-16 23:52:04

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

CL's tagbody is a convenient target:

(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))))
淡紫姑娘! 2024-10-16 23:52:04

我会选择延续,就像这个伪方案示例一样。

只需将当前执行点存储在延续中,并在适当的时候调用它即可。

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

I'd go for continuations like in this pseudo-scheme example.

Just store the current point of execution in a continuation and call it when appropriate.

(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))))))))
酒几许 2024-10-16 23:52:04

我知道这已经晚了 8 年,但我认为这可能会对某人有所帮助。

使用 Common Lisp 中的 iterate 构造,您可以使用 next-iteration 子句:

(iter (for i from 0 to 100) 
  (if (isprime i)
      (next-iteration)
      (do-something-else)))

I know this is 8 years late, but I thought it might help someone.

Using the iterate construct in Common Lisp, you could use the next-iteration clause:

(iter (for i from 0 to 100) 
  (if (isprime i)
      (next-iteration)
      (do-something-else)))
゛时过境迁 2024-10-16 23:52:04

实现这一点的简单的方案方法就是重构你的代码:

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

如果

(let loop ((i 0))
  (cond
     ((isprime i)       ;; continue the loop
         (loop (+ i 1))) 
     ((is_bad i)
         #f)            ;; break from the loop
     (else              ;; do something with i
         .......        ;; and then continue the loop
         (loop (+ i 1)))))

你的循环体是混乱的,并且你想从其内部深处(继续)(break)嵌套结构,要么让编译器以上述方式重组它,要么您可以使用 call/cc 设置退出点,例如

(call/cc (lambda (break)
  (let loop ((i 0))
    (call/cc (lambda (continue)
        ;; your loop logic here, 
        ;; potentially using
        ;;    (continue A)     ;; A is ignored
        ;; or
        ;;    (break B)        ;; B is immediately returned as
        ;;                     ;;   the overall loop's result
        ))
    ;; (continue _) continues here:
    (loop (+ i 1)))))

Racket 具有分隔的延续,这应该更有效。

The straightforward Scheme way to achieve this is just to restructure your code:

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

is

(let loop ((i 0))
  (cond
     ((isprime i)       ;; continue the loop
         (loop (+ i 1))) 
     ((is_bad i)
         #f)            ;; break from the loop
     (else              ;; do something with i
         .......        ;; and then continue the loop
         (loop (+ i 1)))))

If your loop body is tangled and you want to (continue) or (break) from deep inside its nested structure, either have your compiler restructure it in the above way, or you could set up exit points with call/cc as e.g.

(call/cc (lambda (break)
  (let loop ((i 0))
    (call/cc (lambda (continue)
        ;; your loop logic here, 
        ;; potentially using
        ;;    (continue A)     ;; A is ignored
        ;; or
        ;;    (break B)        ;; B is immediately returned as
        ;;                     ;;   the overall loop's result
        ))
    ;; (continue _) continues here:
    (loop (+ i 1)))))

Racket has delimited continuations which should be more efficient.

冰雪梦之恋 2024-10-16 23:52:04

要在Scheme中实现这个特定的代码示例,您不需要continuebreakcall/cc

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

To implement this particular code sample in Scheme, you don't need continue, break or call/cc:

(let loop ((i 0))
  (when (< i 100)
      (if (prime? i)
          (loop (add1 i)))
      (do-something-else)))
岁月苍老的讽刺 2024-10-16 23:52:04

我认为维杰的答案可以以一种有效的方式扩展(很抱歉回答我自己的问题,但无法弄清楚如何在评论中格式化代码):

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

它不是一个宏,但无疑会导致一个足够通用的宏。我很想看到任何改进。我对计划还很陌生。

I think Vijay's answer can be extended in a way that works (sorry for answering my own question, but can't figure out how to format code in a comment):

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

It's not a macro, but doubtlessly leads to one that's general enough. I'd be interested to see any improvements. I'm pretty new to Scheme.

绾颜 2024-10-16 23:52:04

您也可以使用 throwcatch (elisp):

(let ((j 0))
  (while (< j 10)
    (catch 'continue
      (setq j (1+ j))
      (if (= j 3) (throw 'continue t))
      (prin1 j))))
1245678910nil

You can use throw and catch as well (elisp):

(let ((j 0))
  (while (< j 10)
    (catch 'continue
      (setq j (1+ j))
      (if (= j 3) (throw 'continue t))
      (prin1 j))))
1245678910nil
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文