我可以为一个带有可变数量参数的Scheme(Racket)结构编写构造函数吗?

Eri*_*rom 6 scheme constructor struct variadic-functions racket

我理解如何编写一个使用点表示法获取任意数量参数的函数.示例:(define (func-name . args) func-body).

我理解如何使用构造函数guard来预处理构造函数参数,允许我将不同的类型传递给构造函数.例:

(struct struct-id (field-ids)
    #:guard (lambda (field-ids type-name) process-fields))
Run Code Online (Sandbox Code Playgroud)

但那就像我能得到的那样接近.你能写一个带有任意数量参数的守卫吗?或者是否有其他方法来修改结构构造函数的作用?

Thr*_*unt 3

只需编写一个包装器:

(struct struct-id (a b c d) #:constructor-name struct-id*
      #:guard (lambda (a b c d type-name) do-stuff))

(define (struct-id (a) (b) (c) (d 'default-value))
        (struct-id* a b c d))
Run Code Online (Sandbox Code Playgroud)

这为您提供了一个构造函数,其中所有字段参数都是可选的。以这种方式定义它们而不是使用点符号可以使您不必解析其余参数。

我为 提供了一个默认值d,Racket 将为其他的提供默认值#f

您还可以将其定义为具有关键字参数:

(define (struct-id #:a (a #f) #:b (b #f) #:c c #:d (d 'default))
        (struct-id* a b c d))
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,#:c是一个必需的参数,因为我省略了括号,我提供了'default默认值d,其他参数将有一个默认值#f,这次必须显式提供。关键字可以按任何顺序传递给构造函数。

如果您使用大量结构,您可能需要一个宏来为您定义包装器:

(begin-for-syntax
 (require (planet jphelps/loop)) ;; Installs a library on first use. Be patient.
 (define stx-symbol->string (compose symbol->string syntax->datum))
 (define (make-constructor-name stx-name)
    (datum->syntax stx-name
      (string->symbol
       (string-append (stx-symbol->string stx-name) "*"))))
 (define (stx-symbol->stx-keyword stx-symbol)
   (datum->syntax stx-symbol
    (string->keyword
     (symbol->string
      (syntax->datum stx-symbol))))))

(define-syntax struct*
  (lambda (stx)
    (syntax-case stx ()
        ((_ struct-name fields . options)
         #`(begin
             (struct struct-name fields . options)
             (define (#,(make-constructor-name #'struct-name) 
                . #,(loop for name in (syntax-e #'fields)
                        collect (stx-symbol->stx-keyword name)
                        collect #`(#,name #f)))
               (struct-name . fields)))))))
Run Code Online (Sandbox Code Playgroud)

然后像这样定义你的结构:

(struct* struct-id (a b c d) #:guard whatever)
Run Code Online (Sandbox Code Playgroud)

您将自动获得一个基于关键字的构造函数,struct-id*该构造函数与表单生成的名称不冲突struct

编辑

显然,上面的宏最初编写时在基于模块的程序中不起作用。我只在 REPL 上测试过它,它的行为更像 Lisp,因为你可以重新定义事物。struct这掩盖了's#:constructor-name选项添加和附加构造函数名称而不是覆盖现有构造函数名称的事实。尽管事实上有一个#:extra-constructor-name选项也可以创建额外的构造函数名称。

以完全无缝的方式解决此问题需要您重新实现整个struct宏。您必须重命名该结构,然后不仅生成构造函数,还生成所有访问器和修改器。一个更简单的解决方法是生成一个与原始构造函数名称不同的构造函数。我编辑了上面的代码来实现此解决方法。