是否有一种速记方法来更新球拍中的特定结构字段? [英] Is there a shorthand way to update a specific struct field in racket?

查看:45
本文介绍了是否有一种速记方法来更新球拍中的特定结构字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个包含许多字段的结构:

Suppose I have a struct with many fields:

(struct my-struct (f1 f2 f3 f4))

如果我要返回一个更新了 f2 的新结构,我必须改写所有其他字段:

If I am to return a new struct with f2 updated, I have to rephrase every other fields:

(define s (my-struct 1 2 3 4))
(my-struct (my-struct-f1 s)
           (do-something-on (my-struct-f2 s))
           (my-struct-f3 s)
           (my-struct-f4 s))

这是多余的,如果我更新字段的数量或更改它们的顺序,这将成为错误的来源.

Which is redundant and would be a source of bugs if I update the number of the fields or changed their orders.

我真的想知道是否有一种方法可以更新结构的特定字段,例如:

I really wonder if there's a such way I can update a specific field for a struct like:

(my-struct-f2-update (my-struct 1 2 3 4)
                     (lambda (f2) (* f2 2)))
;; => (my-struct 1 4 3 4)

或者我可以将它们设置为一个新值:

Or I can just set them to a new value as:

(define s (my-struct 1 2 3 4)
(my-struct-f2-set s (* (my-struct-f2 s) 2))
;; => (my-struct 1 4 3 4)

注意,这里不是改变smy-struct-f2-updatemy-struct-f2-set 应该只返回一个 sf2 字段已更新.

Notice, this is not mutating s here; my-struct-f2-update and my-struct-f2-set should be just returning a copy of s with f2 field updated.

在 Haskell 中,我知道完成这项工作的 'lens' 库.我只是想知道是否有一些类似的方法可以用于球拍.谢谢.

In Haskell I know the 'lens' library that does this job. I'm just wondering if there are some similar ways that I can adopt for racket. Thanks.

推荐答案

你知道吗?这是一个非常好的主意.事实上,在一些情况下我想要这个功能,但我没有.坏消息是 Racket 没有提供任何此类服务.好消息是 Racket 有宏!

You know what? This is a really good idea. In fact, there have been a few cases in which I wanted this functionality, but I didn't have it. The bad news is that nothing of this sort is provided by Racket. The good news is that Racket has macros!

我向你展示define-struct-updaters

(require (for-syntax racket/list
                     racket/struct-info
                     racket/syntax
                     syntax/parse))

(define-syntax (define-struct-updaters stx)
  (syntax-parse stx
    [(_ name:id)
     ; this gets compile-time information about the struct
     (define struct-info (extract-struct-info (syntax-local-value #'name)))
     ; we can use it to get the constructor, predicate, and accessor functions
     (define/with-syntax make-name (second struct-info))
     (define/with-syntax name? (third struct-info))
     (define accessors (reverse (fourth struct-info)))
     (define/with-syntax (name-field ...) accessors)
     ; we need to generate setter and updater identifiers from the accessors
     ; we also need to figure out where to actually put the new value in the argument list
     (define/with-syntax ([name-field-set name-field-update
                           (name-field-pre ...) (name-field-post ...)]
                          ...)
       (for/list ([accessor (in-list accessors)]
                  [index (in-naturals)])
         (define setter (format-id stx "~a-set" accessor #:source stx))
         (define updater (format-id stx "~a-update" accessor #:source stx))
         (define-values (pre current+post) (split-at accessors index))
         (list setter updater pre (rest current+post))))
     ; now we just need to generate the actual function code
     #'(begin
         (define/contract (name-field-set instance value)
           (-> name? any/c name?)
           (make-name (name-field-pre instance) ...
                      value
                      (name-field-post instance) ...))
         ...
         (define/contract (name-field-update instance updater)
           (-> name? (-> any/c any/c) name?)
           (make-name (name-field-pre instance) ...
                      (updater (name-field instance))
                      (name-field-post instance) ...))
         ...)]))

如果您不熟悉宏,它可能看起来有点吓人,但它实际上并不是一个复杂的宏.幸运的是,您无需了解它的工作原理即可使用它.这样做的方法如下:

If you're not familiar with macros, it can look a little intimidating, but it's actually not a complicated macro. Fortunately, you don't need to understand how it works to use it. Here's how you'd do that:

(struct point (x y) #:transparent)
(define-struct-updaters point)

现在您可以随意使用所有相关的功能设置器和更新器.

Now you can use all the relevant functional setters and updaters as you'd please.

> (point-x-set (point 1 2) 5)
(point 5 2)
> (point-y-update (point 1 2) add1)
(point 1 3)

我相信已经有一些重新设计 Racket 结构系统的理论计​​划,我认为这将是一个有价值的补充.在此之前,请随意使用此解决方案.我已将此答案中的代码作为 struct-update 提供包,可以使用 raco pkg install struct-update 安装.

I believe there have been some theoretical plans to redesign the Racket struct system, and I think this would be a valuable addition. Until then, feel free to use this solution. I’ve made the code in this answer available as the struct-update package, which can be installed using raco pkg install struct-update.

这篇关于是否有一种速记方法来更新球拍中的特定结构字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆