【问题标题】:Defining a For Loop in Scheme在 Scheme 中定义一个 For 循环
【发布时间】:2014-01-15 15:24:53
【问题描述】:

注意:这似乎是 Gauche Scheme 版本 0.9.3.3。
我似乎无法理解这些 Lisp 语言:/。

我正在尝试在 Scheme 中定义 for 循环语法。我不确定这是否可以通过递归函数实现(我认为可以),但此时我真的想让define-syntax 工作。

我可以用这段代码让循环运行一次(然后它看起来像终止了):

(define-syntax forloop
  (syntax-rules ()
    ((forloop start stop exps)
      (letrec ((aloop (lambda (astart astop aexps)
        (if (<= astart astop) (begin aexps (aloop (+ astart 1) astop aexps))))))
          (aloop start stop exps)))))

如果还能够用省略号定义它会很好,但这会给出“模板包含常量形式的重复”。我已经阅读了 R5RS 规范的宏部分,不过我很快就会重新阅读模板部分:

(define-syntax forloop
  (syntax-rules ()
    ((forloop start stop exps ...)
      (letrec ((aloop (lambda (astart astop aexps ...)
        (if (<= astart astop) (begin aexps ... (aloop (+ astart 1) astop aexps ...))))))
        (aloop start stop exps ...)))))

我先尝试了这个,但它运行了大约 10 秒,然后没有任何输出就失败了……我正在使用compileonline.com,所以这可能与它有关:

(define-syntax forloop
  (syntax-rules ()
    ((forloop start stop exps ...) 
      (if (<= start stop) 
        (begin exps ... (forloop (+ start 1) stop exps ...))))))

如果我从不调用 forloop 没有问题(我认为这是因为它永远不必扩展宏),所以我用来测试这些的代码是:

(forloop 6 8 (display "g"))

我做错了什么?我已经成功地实现了一个when 声明(我自己,但那里有几十个例子,我已经看过很多)。我认为我想做的事情的递归性质和省略号把我搞砸了。

【问题讨论】:

    标签: for-loop scheme lisp define-syntax


    【解决方案1】:

    宏在编译时被扩展,因此像(begin exps ... (forloop (+ start 1) stop exps ...)) 这样的东西会一次又一次地扩展forloop,而不管(+ start 1) 的值是什么(在运行时评估)。

    也许你能做的最好的事情,至少对于syntax-rules,是使用宏只捕获要运行的表达式,并使用非宏代码来处理循环:

    (define-syntax forloop
      (syntax-rules ()
        ((forloop start stop exps ...)
         (let ((j stop))
           (let loop ((i start))
             (when (<= i j)
               exps ...
               (loop (+ i 1))))))))
    

    您也可以使用do 循环:

    (define-syntax forloop
      (syntax-rules ()
        ((forloop start stop exps ...)
         (let ((j stop))
           (do ((i start (+ i 1)))
               ((> i j))
             exps ...)))))
    

    【讨论】:

    • 谢谢,这行得通,编译时间与运行时间是有道理的,但我看不出你的第一个例子与我的第一个例子有什么根本不同......你的工作和我的工作不会大声笑。
    • 根本区别在于我的版本中的loop不是宏。
    • letrec 是否在创建宏?或者也许是 lambda?我不认为这是一个宏。
    • 糟糕,我正在查看您的最后一个示例,而不是第一个示例。与您的第一个示例的根本区别在于我没有将表达式传递给递归调用。
    • @MillieSmith 您的第一个代码将(forloop a b c) 变成(letrec ((aloop (lambda(x y z) (if (&lt;= x y) (begin z (aloop (+ x 1) y z)))))) (aloop a b c))。所以c 被评估一次,它的value 被传递到aloop 调用中,它不做任何事情。您的 aloop 只是一个函数 - 因此获取它的参数 values; Chris 的宏创建了一个命名的 let 构造,在其中放置您的表达式,如 expressions
    猜你喜欢
    • 2014-08-15
    • 2011-02-19
    • 1970-01-01
    • 1970-01-01
    • 2020-04-15
    • 1970-01-01
    • 1970-01-01
    • 2022-11-04
    • 1970-01-01
    相关资源
    最近更新 更多