Using built-in math operators with custom struct

强颜欢笑 提交于 2019-12-10 13:44:17

问题


I want to be able to do something like this:

(struct point (x y))

(define p1 (point 1 2))
(define p2 (point 10 20))

(+ p1 p2)  ; -> (point 11 22)

Is it possible to teach a struct like point to work with built-in math operators like +?

The docs seem to manage to implement custom (equal? ...) handling in section 5.5 on this page. What I'm trying to do is quite similar ...

Or should I just define function like (point-add p1 p2)?


回答1:


You can either

  1. Go with point-add
  2. Use your own + that matches against all possible value types that you want to take on. This is sufficient if you know all possible value types beforehand, but it wouldn't be easy to extend it to include newly created struct definitions in client's code. For example:

    ;; We will "shadow" Racket's + with our own +, but we still
    ;; need the functionality of Racket's +, so let's require
    ;; Racket's + but use the name racket:+ instead
    (require (only-in racket/base [+ racket:+]))
    
    (struct point (x y) #:transparent)
    
    (define (+ x y)
      (match* (x y)
        [((point a b) (point c d)) (point (+ a c) (+ b d))]
        [((point _ _) _) (error '+ "Can't add a point with non point")]
        [(_ (point _ _)) (error '+ "Can't add a point with non point")]
        [(_ _) (racket:+ x y)]))
    
    ;; in client's code
    
    (+ (point 1 2) (point 3 4)) ;=> (point 4 6)
    (+ 1 2)                     ;=> 3
    
  3. Define a new generics so that we can do something similar to gen:equal+hash for equal?. For example:

    (require racket/generic
             (only-in racket/base [+ racket:+]))
    
    (define-generics addable
      (add addable _)
      #:fast-defaults ([number?
                        (define (add x y) (racket:+ x y))]))
    
    (define + add)
    
    ;; in client's code
    
    (struct point (x y)
      #:transparent
      #:methods gen:addable
      [(define (add x y)
         (match* (x y)
           [((point a b) (point c d)) (point (+ a c) (+ b d))]
           [(_ _) (error 'add "Can't add a point with non point")]))])
    
    (struct point-3d (x y z)
      #:transparent
      #:methods gen:addable
      [(define (add x y)
         (match* (x y)
           [((point-3d a b c) (point-3d d e f))
            (point-3d (+ a d) (+ b e) (+ c f))]
           [(_ _) (error '+ "Can't add a point-3d with non point-3d")]))])
    
    (+ (point 1 2) (point 3 4)) ;=> (point 4 6)
    (+ (point-3d 1 2 3) (point-3d 4 5 6)) ;=> (point-3d 5 7 9)
    (+ 1 2) ;=> 3
    
  4. To accept multiple arguments, modify (3) as follows

    (define +
      (case-lambda
        [() 0]
        [(x . xs) (foldl add x xs)]))
    
    ;; client's code
    
    (+ (point 1 2) (point 3 4) (point 5 6)) ;=> (point 9 12)
    (+ 1 2 3) ;=> 6
    (+) ;=> 0
    (+ 1) ;=> 1
    (+ (point-3d 1 2 3)) ;=> (point-3d 1 2 3)
    


来源:https://stackoverflow.com/questions/55876455/using-built-in-math-operators-with-custom-struct

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!