重载结构构造函数?
Overloading a struct constructor?
我的问题与 here 的问题相同,但那里给出的答案实际上不起作用。
我有一堆继承自父结构 A
的结构,它有两个字段,我想为其所有后代提供可选字段。以前我使用的是 #:auto
,但事实证明这并不是我想要的,因为它破坏了 struct-copy
之类的方法,而且我确实希望能够在结构创建时有选择地为这些字段提供值.
我发现了一些关于结构的可选参数的其他问题,但答案都建议定义自定义构造函数并改用它。不幸的是,我已经有很多使用常规构造函数的代码,所以我真正想要的是让这个自定义构造函数与结构本身同名。我链接的问题的答案正是我要找的,但不幸的是它只在 REPL 中有效,因为允许重复定义。在 REPL 之外,我收到类似 module: duplicate definition for identifier in: exn:my-app
的错误(当 运行 链接问题的答案时)。
编辑:我知道重复定义问题是因为结构 id 也绑定到转换器。我不想阻止这个定义的发生;我想要一个名称绑定到该结构的构造函数和转换器,其中构造函数不是默认构造函数。
有没有一种方法可以很好地与现有代码配合使用?
struct
构造将定义两个具有结构名称的实体。具有结构信息的构造函数和转换器绑定。
为了避免 "duplicate identifier" 错误,您可以使用 #: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"
进一步研究 soegaard 提到的新结构关键字,我想我想出了一个更简洁的解决方案,更重要的是,更容易抽象成一个宏(那些模块和要求给了我一个非常艰难的时期)。我想我会分享它以供将来参考。同样,这需要每晚 Racket 构建之一。我正在使用 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 也可以用作标识符,没有任何语法问题。我真的不认为那部分有多大实际用处,但我觉得它很好。
我的问题与 here 的问题相同,但那里给出的答案实际上不起作用。
我有一堆继承自父结构 A
的结构,它有两个字段,我想为其所有后代提供可选字段。以前我使用的是 #:auto
,但事实证明这并不是我想要的,因为它破坏了 struct-copy
之类的方法,而且我确实希望能够在结构创建时有选择地为这些字段提供值.
我发现了一些关于结构的可选参数的其他问题,但答案都建议定义自定义构造函数并改用它。不幸的是,我已经有很多使用常规构造函数的代码,所以我真正想要的是让这个自定义构造函数与结构本身同名。我链接的问题的答案正是我要找的,但不幸的是它只在 REPL 中有效,因为允许重复定义。在 REPL 之外,我收到类似 module: duplicate definition for identifier in: exn:my-app
的错误(当 运行 链接问题的答案时)。
编辑:我知道重复定义问题是因为结构 id 也绑定到转换器。我不想阻止这个定义的发生;我想要一个名称绑定到该结构的构造函数和转换器,其中构造函数不是默认构造函数。
有没有一种方法可以很好地与现有代码配合使用?
struct
构造将定义两个具有结构名称的实体。具有结构信息的构造函数和转换器绑定。
为了避免 "duplicate identifier" 错误,您可以使用 #: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
.
#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"
进一步研究 soegaard 提到的新结构关键字,我想我想出了一个更简洁的解决方案,更重要的是,更容易抽象成一个宏(那些模块和要求给了我一个非常艰难的时期)。我想我会分享它以供将来参考。同样,这需要每晚 Racket 构建之一。我正在使用 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 也可以用作标识符,没有任何语法问题。我真的不认为那部分有多大实际用处,但我觉得它很好。