Adding structure fields

So, I have Racket struct

, stats:

(struct stats (str con dex int wis cha))

      

And I have a function add-stats

:

(define (modify-stats mods base)
   (stats (+ (stats-str mods)
             (stats-str base))
          (+ (stats-con mods)
             (stats-con base))
          (+ (stats-dex mods)
             (stats-dex base))
          (+ (stats-int mods)
             (stats-int base))
          (+ (stats-wis mods)
             (stats-wis base))
          (+ (stats-cha mods)
             (stats-cha base))))

      

Obviously this is really messy and there is a lot of unwanted repetition. I managed to shorten it to a more readable version:

(define (modify-stats mods base)
    (define (add-stat statid)
        (+ (statid mods)
           (statid base)))

    (stats (add-stat stats-str)
           (add-stat stats-con)
           (add-stat stats-dex)
           (add-stat stats-int)
           (add-stat stats-wis)
           (add-stat stats-cha)))

      

But there are still many repetitions of "stat (s)". Is there a cleaner way I can perform operations on fields of two structures of the same type?


UPDATE: I managed to do it a little better:

(define (stat a-stat stats)
  (match a-stat
    ["str" (stats-str stats)]
    ["con" (stats-con stats)]
    ["dex" (stats-dex stats)]
    ["int" (stats-int stats)]
    ["wis" (stats-wis stats)]
    ["cha" (stats-cha stats)]
    [_ (error "Not a stat!")]))

(define (modify-stats mods base)
  (define (add-stat string)
    (+ (stat string mods)
       (stat string base)))
  (stats (add-stat "str")
         (add-stat "con")
         (add-stat "dex")
         (add-stat "int")
         (add-stat "wis")
         (add-stat "cha")))

      

+3


source to share


1 answer


Without a second thought, here's one way to do it:

(define (modify-stats mods base)
  (define (get-fields obj)
    (map (lambda (getter) (getter obj))
         (list stats-str stats-con stats-dex stats-int stats-wis stats-cha)))
  (apply stats (map + (get-fields mods) (get-fields base))))

      




Loath, since I suggest using macros to improve performance, this macro generates exactly the same code as the OP's first version:

(require (for-syntax racket/syntax))
(define modify-stats
  (let-syntax
      ((bump (lambda (stx)
               (define (bump-attr attr)
                 (with-syntax ((getter (format-id attr "stats-~a" attr #:source attr)))
                   #'(+ (getter mods) (getter base))))
               (syntax-case stx ()
                 ((_ attr ...)
                  (with-syntax (((bumped ...) (map bump-attr (syntax->list #'(attr ...)))))
                    #'(lambda (mods base)
                        (stats bumped ...))))))))
    (bump str con dex int wis cha)))

      

+4


source