【问题标题】:Scheme/"The Seasoned Schemer": Question about the syntax of the definition of "try" functionScheme/“The Seasoned Schemer”:关于“try”函数定义语法的问题
【发布时间】:2020-02-27 14:23:28
【问题描述】:

在他们的“经验丰富的计划者”一书中,Felleisen 和 Friedman 介绍了 try 函数。根据http://community.schemewiki.org/?seasoned-schemer,这个函数可以定义为

(define-syntax try
  (syntax-rules ()
    ((try var a . b)
     (letcc success 
       (letcc var (success a)) . b))))

letcc 定义为

(define-syntax letcc 
  (syntax-rules () 
    ((letcc var body ...) 
     (call-with-current-continuation 
       (lambda (var)  body ... ))))) 

现在,虽然我了解 try 的作用以及如何使用它,但我很难遵循它的正式定义。行中letccsuccess(letcc var (success a)) . b的应用中点的具体含义是什么

(letcc success 
       (letcc var (success a)) . b)

try?或者换一种问法:如果在a 中调用vartry 定义的哪一部分确定try 被评估为b

编辑 1: 抱歉,letcc 的定义不完整。添加了缺少的第一行。

编辑2:以下代码可以在Racket中运行。

(define-syntax letcc
   (syntax-rules ()
                 ((letcc var body ...)
                  (call-with-current-continuation
                    (lambda (var)  body ... )))))

 (define-syntax try
   (syntax-rules ()
                 ((try var a . b)
                  (letcc success
                         (letcc var (success a)) . b))))

(try var (+ 1 1) 4)
; output: 2

(try var (var '(2)) 4)
; output: 4

【问题讨论】:

  • 请粘贴完整的工作代码。但是你可以在 mit-scheme 中使用unsyntax 来查看宏扩展器是如何去除语法糖的。

标签: syntax scheme seasoned-schemer


【解决方案1】:

语法规则是模式匹配。一个点表示一对中的carcdr,就像lambda / define 中的其余参数一样:

(define (my-list . args)
  args)

列表只是嵌套的对。例如。 (1 2 3) 只是显示(1 . (2 . (3 . ()))) 的奇特方式。

所以(this is random symbols in a list) 将匹配(try var a . b) 通过try 匹配thisis 匹配varrandom 匹配a,并且(symbols in a list) 匹配b

当您在扩展中看到相同的内容时,这意味着代码应该在点之后拼接匹配项。例如(var . b) 与前面的例子变成(is symbols in a list)。它类似于拥有b ...,但对系统来说更便宜。

【讨论】:

  • 谢谢;不幸的是,这不是我要问的。我理解(try var a . b) 模式中的点是什么意思,但我的问题是关于该模式下方两行的点。
  • 再次感谢,最后一段现在回答了我关于扩展中的点的问题,即它是一个拼接指令。你知道这个扩展规则在任何 Scheme 语言参考中的解释吗?
  • @J.Hauser 不是指令。如果您将'(a . b) 作为代码提供给读者,它将在编译器或运行时执行(cons a b)。具有(a b) 的替换将意味着(cons a (cons b '()))(a . b) 将是(cons a b) 并且当读者阅读了语法规则时,它已经被consed。因此,在扩展过程中,它只是用代码的“绑定”替换它找到的逐字复制结构的符号。 The reference doesn't use much time on . at all.
【解决方案2】:

我不是 Scheme 宏语法复杂性方面的专家,但我认为try 的等效定义是:

(define-syntax try 
  (syntax-rules () 
    ((try var a b ...) 
     (letcc success 
       (letcc var (success a)) b ...))))

我确实觉得这更容易阅读。

(try e <a> <b> <c>)(任一版本,至少在 Racket 中)然后扩展为

(letcc success
  (letcc e
    (success <a>))
  <b> <c>)))

因此,当&lt;a&gt; 被评估时,e 是一个延续,它从内部letcc 形式返回其参数,它们被忽略。如果e 被调用,那是你结束的地方,然后&lt;b&gt;&lt;c&gt; 以正常方式得到评估(我只放了不止一件事,因为我可以,它处理整个. ... 东西)。如果在评估&lt;a&gt; 的过程中没有调用e ,则调用success ,这也是一个延续,然后返回评估@ 的结果987654336@ 来自整个表格。

至少我认为是这样的。


下面是我用来测试我理解的东西的一部分 Racket。

(module+ test
  (require rackunit))

(define-syntax let/cc 
  (syntax-rules () 
    ((letcc var body ...) 
     (call-with-current-continuation 
       (lambda (var) body ... ))))) 

(define-syntax try 
  (syntax-rules () 
    ((try var a b ...) 
     (let/cc success 
       (let/cc var (success a)) b ...))))

(module+ test
  (check-eqv?
   (try fail (+ 1 1) 4)
   2)

  (check-eqv?
   (try fail (fail '(2)) 4)
   4)

  (check-eqv?
   (try fail
        (begin 1 (fail) (error "failed to fail"))
        4 5 6)
   6))

【讨论】:

    【解决方案3】:

    让我们试试看会发生什么。我正在使用 mit-scheme。

    文件try.scm:

    (define-syntax letcc
       (syntax-rules ()
                     ((letcc var body ...)
                      (call-with-current-continuation
                        (lambda (var)  body ... )))))
    
     (define-syntax try
       (syntax-rules ()
                     ((try var a . b)
                      (letcc success
                             (letcc var (success a)) . b))))
    
    (try var (+ 1 1) 4)
    
    (try var (var '(2)) 4)
    

    第一步:编译文件:

    (sf "尝试")

    这将生成try.bin

    第二步,打印脱糖语法:

    (pp (unsyntax (fasload "try")))

    ;Loading "try.bin"... done
     ................
     (call-with-current-continuation
      (lambda (success)
        (call-with-current-continuation (lambda (var) (success (+ 1 1))))
        4))
     (call-with-current-continuation
      (lambda (success)
        (call-with-current-continuation (lambda (var) (success (var '(2)))))
        4)))
    

    现在您可以清楚地看到执行了什么以及结果。

    (try var (+ 1 1) 4) 的情况下,您会跳出2 个嵌套的calcc,当您调用success 时,其值为2,而在(try var (var '(2)) 4) 中,您会跳出1 级和序列中的4将返回第一个延续的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-22
      • 1970-01-01
      • 2011-09-10
      相关资源
      最近更新 更多