【问题标题】:How to fix this Scheme coroutine code to run in Racket如何修复此 Scheme 协程代码以在 Racket 中运行
【发布时间】:2019-01-12 10:05:08
【问题描述】:

我正在尝试使用来自this accepted answer 的方案实现在 Racket LISP 中为个人学习项目实现协程。但是,将我的 .rkt 文件加载到球拍 repl 中时,出现以下错误:

; 3.rkt:111:18: define: not allowed in an expression context
;   in: (define (run-handler) (make-generator (lambda (yield) (send
;     (get-dp-data-object key) run))))

似乎在抱怨这部分代码中的定义:

108 (define-syntax (define-coroutine stx)                                                               
109   (syntax-case stx ()                                                           
110                ((_ (name . args) . body )                                       
111                 #`(define (name . args)                                         
112                     (make-generator                                             
113                       (lambda (#,(datum->syntax stx 'yield))                    
114                         . body))))))                                

根据accepted answer here,Scheme 不共享这个确切的错误,并且在尝试在表达式中定义时是 Racket 所独有的。

代码调用(define-coroutine)似乎是:

518     ;; Qt-esque connect macro                                                   
519     (define-syntax connect-message                                              
520       (syntax-rules ()                                                          
521                     [(register src-obj-key msg-type dst-obj-key handler)        
522                      (register-message-handler                                  
523                        msg-type                                                 
524                        (begin                                                   
525                          (define-coroutine                                      
526                            (handler-accessor)                                                       
527                            (if (eqv? (get-dp-data-object dst-obj-key) #f)       
528                              #f                                                 
529                              (send                                              
530                                (get-dp-data-object dst-obj-key)                 
531                                handler                                          
532                                (get-field args msg))))                          
533                          handler-accessor))]))                                  

这是我的第一个 Racket 项目,所以我在学习很多。上面的(开始)是试图定义并返回一个调用对象方法的协程。我确信这段代码 sn-p 有很多问题,但是调试器阻止了我上面的问题,阻止我得到以后的错误:)

我在 Racket、Scheme 或 LISP 方面几乎不够熟练,无法解决这个问题,目前我几乎无法理解这个错误。有人可以为我解决这个问题并希望能纠正这个问题,以便我可以让这个协程代码在 Racket 中工作吗?

【问题讨论】:

  • 既然define-coroutine的body从来没有yield什么的,那么使用它的目的是什么?你不能用(lambda () same-body)替换整个开头吗?

标签: scheme lisp racket


【解决方案1】:

在球拍中,begin 不会创建新的范围。1这意味着您在 begin 表单内定义的任何内容仍在该表单之外的范围内。这也意味着 begin 不会改变上下文,因此如果您在表达式上下文中,begin 会保留它。

define 形式(大致)具有以下语法:

(define <id> <expr>)

其中&lt;id&gt; 是变量名,&lt;expr&gt; 是表达式。然而,define 表单本身不是一个表达式,所以这样的事情是无效的:

(define x (define y 5))

而且因为 begin 不会改变上下文,所以这也是无效的:

(define x
  (begin
    (define y 5)
    y))

相反,您可以使用let 创建新范围。因为 let 本身是一个表达式,所以你可以把它放在 define 中。所以你可以这样写:

(define x
  (let ()
    (define y 5)
    y))

现在,x 按预期绑定到5

回到你原来的问题,你有代码:

518     ;; Qt-esque connect macro                                                   
519     (define-syntax connect-message                                              
520       (syntax-rules ()                                                          
521                     [(register src-obj-key msg-type dst-obj-key handler)        
522                      (register-message-handler                                  
523                        msg-type                                                 
524                        (begin                                                   
525                          (define-coroutine                                      
526                            (handler-accessor)                                                       
527                            (if (eqv? (get-dp-data-object dst-obj-key) #f)       
528                              #f                                                 
529                              (send                                              
530                                (get-dp-data-object dst-obj-key)                 
531                                handler                                          
532                                (get-field args msg))))                          
533                          handler-accessor))]))                                  

我假设register-message-handler 是一个函数,因此需要一个表达式。但是你有define-coroutine,他的详细说明是define 表格。因此,您可以使用let 将其转换为表达式,而不是使用begin,从而为您提供如下内容:

(register-message-handler                                  
  msg-type                                                 
  (let ()                                                   
    (define-coroutine (handler-accessor) ....)
    handler-accessor))                                

1糟糕的设计决策……我知道。几十年前的桥下的水。 :(

【讨论】:

猜你喜欢
  • 2012-02-07
  • 2012-04-19
  • 2019-05-25
  • 1970-01-01
  • 1970-01-01
  • 2016-02-05
  • 1970-01-01
  • 1970-01-01
  • 2019-06-12
相关资源
最近更新 更多