重载结构构造函数?

时间:2016-06-30 19:43:51

标签: racket

我的问题与here提出的问题相同,但在那里给出的答案实际上并不起作用。

我有一堆继承自父结构A的结构体,它有两个字段,我想让它的所有后代都是可选的。以前我使用的是#:auto,但事实证明这并不是我想要的,因为它打破了像struct-copy这样的方法,而且我确实希望能够在结构创建时为这些字段提供值

我发现了一些关于结构的可选参数的其他问题,但是答案都建议定义一个自定义构造函数并使用它。不幸的是,我已经有很多使用常规构造函数的代码,所以我真正想要的是这个自定义构造函数与结构本身具有相同的名称。我链接的问题的答案是我正在寻找的,但不幸的是它只适用于REPL,因为允许重复的定义。在REPL之外,我得到一个错误,例如module: duplicate definition for identifier in: exn:my-app,当运行链接问题的答案时。

编辑:我知道重复的定义问题是因为s​​truct id也绑定到了一个转换器。我不想阻止这个定义的发生;我想要一个绑定到该结构的构造函数和变换器的名称,其中构造函数不是默认值。

有没有办法可以很好地使用现有代码?

2 个答案:

答案 0 :(得分:4)

进一步研究soegaard提到的新结构关键字,我想我已经提出了一个更清洁的解决方案,更重要的是,更容易抽象到宏(这些模块和需求是给我一个非常艰难的时间)。我以为我会分享它以供将来参考。同样,这需要一个夜间球拍构建。我正在使用6.5.0.7。

(注意:在这里使用case-lambda而不是定义关键字参数只是我的偏好。)

#lang racket

(require (for-syntax syntax/transformer))

(struct horse (color)
  #:name Horse
  #:constructor-name Horse
  #:transparent)

(define make-horse
  (case-lambda
    [() (Horse "black")]
    [(color) (Horse color)]))

(define-match-expander horse
  ; match expander
  (λ (pat)
    (syntax-case pat ()
      [(_ more ...) #'(Horse more ...)]))

  ; constructor
  ; edit: changing this to use make-variable-like-transformer,
  ;  as suggested in the comments.
  #;(syntax-id-rules ()
      [(_ args ...) (make-horse args ...)]
      [horse Horse])
  (make-variable-like-transformer #'make-horse))

样本用法:

> (define black-beauty (horse))
> black-beauty
(horse "black")

> (define ginger (horse "red"))
> ginger
(horse "red")

> (match black-beauty
    [(horse color) color])
"black"

> (match ginger
    [(horse color) color])
"red"

> (horse-color black-beauty)
"black"

> (horse-color ginger)
"red"

简而言之:如果您刚刚使用struct,实例化,匹配和访问字段似乎可以正常工作。 struct id也可以用作标识符而不会出现任何语法问题。我并不认为这部分有很多实际用途,但我觉得它很不错。

答案 1 :(得分:3)

struct构造将定义两个具有struct名称的实体。构造函数和变换器绑定,包含结构信息。

为了避免重复的标识符"错误您可以使用#:omit-define-syntaxes

另一种方法是在子模块中定义结构并仅导出您需要的东西(并可能重命名某些标识符)。

#lang racket
(struct horse (color)
  #:constructor-name make-horse
  #:omit-define-syntaxes
  #:transparent)

(define (horse #:color [color "black"])
  (make-horse color))

(horse)
(horse #:color "red")

输出:

(horse "black")
(horse "red")

修改

在匹配扩展器的帮助下,可以使用匹配的解决方案。 注意:由于使用了#:extra-name

,因此至少需要6.5版才能生效
#lang racket

(module horse racket
  (provide (struct-out Horse)
           make-horse)
  (struct horse (color)
    #:extra-name Horse
    #:extra-constructor-name make-horse
    #:transparent))

(require 'horse)

; the custom horse constructor
(define (my-make-horse #:color [color "black"])
  (make-horse color))

(define-match-expander horse
  ; match expander
  (λ (pat)
    (syntax-case pat ()
      [(_horse more ...)
       #'(Horse more ...)]))
  ; constructor
  (λ (stx)
    (syntax-case stx ()
      [(_horse arg ...)
       (syntax/loc stx
         (my-make-horse arg ...))])))

(horse)
(horse #:color "red")

(match (horse #:color "blue")
  [(horse color) color])

输出:

(horse "black")
(horse "red")
"blue"