gpt4 book ai didi

racket - 如何访问包含省略号的 `~optional` 中的语法类属性?

转载 作者:行者123 更新时间:2023-12-04 03:25:14 26 4
gpt4 key购买 nike

TL;博士

我正在尝试扩展可选省略号 (~optional datum:my-class ...)转换为使用 my-class 中的属性的表单像这样:#'(list datum.attr ...) .

但由于表单是可选的,当它不存在时,它会解析为 #f ,省略号模式不喜欢:

;; ?: attribute contains non-list value
;; value: #f

我尝试使用 #:defaults ~optional 的论点,像这样:
(~optional datum:my-class ... #:defaults ([(datum 1) null]))
;; which works for usage:
#'(list datum ...)
;; but not for:
#'(list datum.attr ...)

我通过使用这个技巧让它工作:
(~optional datum:my-class ...)

#'(list #,@(if (attribute datum) #'(datum.attr ...) #'()))

我正在尝试寻找是否有更好的方法。

完整的可运行示例

我试着把它保持在最低限度。查看测试子模块。
要查看此问题,请取消注释 parse-bag 的四种实现之一。 ,然后运行 ​​ raco test file.rkt .
#lang racket/base

(provide parse-bag)

(require
(for-syntax racket/base syntax/parse)
syntax/parse)

(struct bag (label objects) #:transparent)
(struct object (name) #:transparent)

(begin-for-syntax
(define-syntax-class obj-exp
#:datum-literals (object name)
(pattern (object (name <name>:str))
#:with result #'(object <name>))))

;; IMPLEMENTATION ONE
;; DESC: The naive but failing approach
;; UNCOMMENT TO TEST
#;(define-syntax (parse-bag stx)
(syntax-parse stx
#:datum-literals (label objects)
[(_ (label <label>:str)
(~optional (objects <object>:obj-exp ...)))
#'(bag <label>
(list <object>.result ...))]))
;; ?: attribute contains non-list value
;; value: #f


;; IMPLEMENTATION TWO
;; DESC: adding defaults will fail too
;; UNCOMMENT TO TEST
#;(define-syntax (parse-bag stx)
(syntax-parse stx
#:datum-literals (label objects)
[(_ (label <label>:str)
(~optional (objects <object>:obj-exp ...)
#:defaults ([(<object> 1) null]))) ; (HERE)
#'(bag <label>
(list <object>.result ...))]))
;; ?: attribute contains non-list value
;; value: #f


;; IMPLEMENTATION THREE
;; DESC: it won't fail when not using syntax-class attributes
;; UNCOMMENT TO TEST
#;(define-syntax (parse-bag stx)
(syntax-parse stx
#:datum-literals (label objects)
[(_ (label <label>:str)
(~optional (objects <object>:obj-exp ...)
#:defaults ([(<object> 1) null])))
#'(bag <label>
(list <object> ...))])) ; (HERE)
;; name: unbound identifier


;; IMPLEMENTATION FOUR
;; DESC: it works, but I find it ugly
;; UNCOMMENT TO TEST
#;(define-syntax (parse-bag stx)
(syntax-parse stx
#:datum-literals (label objects)
[(_ (label <label>:str)
(~optional (objects <object>:obj-exp ...)))
#`(bag <label>
(list #,@(if (attribute <object>)
#'(<object>.result ...)
#'())))]))


(module+ test (require rackunit)

(check-equal?
(parse-bag
(label "Biscuits")
(objects (object (name "Cookie"))
(object (name "Brownie"))))

(bag "Biscuits"
(list (object "Cookie")
(object "Brownie"))))

(check-equal?
(parse-bag (label "Sweets"))
(bag "Sweets" '()))

)

最佳答案

修复“属性值为假”错误的策略有两类:

  • 放置默认值或替代值,以使该属性永远不会为假。
    (a) 使用 ~optional #:defaults(b) 使用 ~or , 可能与 ~parse
    (c) 使用具有多种模式的语法类
  • 处理 body 中为 false 的属性。
    (a) 使用 unsyntax if
    (b) 使用 ~?

  • 1(一)
    问题中的实现 2 和 3,以及 Zoé 答案中的代码,都使用 ~optional #:defaults , 所以他们都试图用 1 (a) 来修复它。
    Zoé 的回答显示了对此的正确使用。要点是,如果你使用像 <object>.result 这样的属性,您需要为 <object>.result 指定默认值,而不仅仅是 <object> .
    但是,如果 obj-exp 中有多个属性,则会出现一个缺点。您需要使用的类。这将要求您为每个指定一个默认值:
    (~optional (objects <object>:obj-exp ...)
    #:defaults ([(<object>.result 1) null]
    [(<object>.x 1) null]))
    1(b)
    如果您使用语法类中的多个属性,例如 <object>.result , <object>.x , <object>.y , <object>.z等,然后 1 (a) 将要求您分别为每个指定默认值。为了避免这种情况,而不是这样写:
    (~optional (objects <object>:obj-exp ...)
    #:defaults ([(<object>.result 1) null]
    [(<object>.x 1) null]
    [(<object>.y 1) null]
    [(<object>.z 1) null]))
    您可以使用 ~or ~parse 像这样:
    (~or (objects <object>:obj-exp ...)
    (~and (~seq) (~parse (<object>:obj-exp ...) null)))
    1(c)
    (define-splicing-syntax-class maybe-objects
    #:datum-literals (objects)
    [pattern (objects <object>:obj-exp ...)]
    [pattern (~seq) #:with (<object>:obj-exp ...) null])
    2 (a) 和 (b)
    问题中的实现 4 使用 unsyntax-splicing if ,所以它是 2 (a) 的一个例子:
    #,@(if (attribute <object>)
    #'(<object>.result ...)
    #'())
    但是,正如您所指出的,这看起来有点难看。它还有另一个问题。如果它本身在一个省略号下,那么它就会失效,因为省略号不会在 #, 中携带它们的效果。或 #,@ .
    这就是为什么 ~? 存在于 2 (b) 中。而不是使用 #,@(if ....) , 您可以使用:
    (~? (<object>.result ...) ())
    这不太有效,但它的这个变体确实有效:
    (~? (list <object>.result ...) (list))
    在实现 4 的变体中使用它:
    (define-syntax (parse-bag stx)
    (syntax-parse stx
    #:datum-literals (label objects)
    [(_ (label <label>:str)
    (~optional (objects <object>:obj-exp ...)))
    #`(bag <label>
    (~? (list <object>.result ...) (list)))]))

    关于racket - 如何访问包含省略号的 `~optional` 中的语法类属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55045447/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com