PasteRack.org
Paste # 11383
2019-02-15 12:55:57

Fork as a new paste.

Paste viewed 274 times.


Embed:

So that's where I went wrong with TR and mutually recursive types

  1. #lang typed/racket
  2.  
  3. ; a naive hack at quadtrees
  4. ; https://www.researchgate.net/profile/Raphael_Finkel/publication/220197855_Quad_Trees_A_Data_Structure_for_Retrieval_on_Composite_Keys/links/0c9605273bba2ece7b000000/Quad-Trees-A-Data-Structure-for-Retrieval-on-Composite-Keys.pdf
  5.  
  6. ; Mutually recursive with the Quadtree type. The node structure means that
  7. ; every node has coordinates embedded, and four children, which may be nodes or nulls.
  8. (struct node ([ NE : Quadtree ]
  9.               [ NW : Quadtree ]
  10.               [ SW : Quadtree ]
  11.               [ SE : Quadtree ]
  12.               [ coords : coordinate]))
  13.  
  14. ; get-node-coords and get-node-child are null-gated wrappers for node accessors.
  15. ; I don't know if this is the accepted way, but it makes the type checker happy.
  16. ; My functions were already nullchecking, but I guess this is more explicit.
  17. (: get-node-coords (-> Quadtree coordinate))
  18. (define (get-node-coords quadtree)
  19.   (cond [(null? quadtree) (raise "Don't be calling get-node-coords on nulls!")]
  20.         [else (node-coords quadtree)]))
  21.  
  22. (: get-node-child (-> Quadtree (-> node Quadtree) Quadtree))
  23. (define (get-node-child quadtree direction)
  24.   (cond [(null? quadtree) (raise "Don't be calling get-node-child on nulls!")]
  25.         [else (direction quadtree)]))
  26.  
  27.  
  28. ; A union type: a Quadtree will be null or a node.
  29. (define-type Quadtree (U Null node))
  30.  
  31. ; I don't know if this will help the compiler optimize, but I figure we don't actually
  32. ; need this to be an integer, so I'm limiting Quadenum to these five Symbol literals.
  33. (define-type Quadenum (U 'NE 'NW 'SW 'SE 'self))
  34.  
  35. ; X and Y coordinates, since Quadtrees' composite keys are two-dimensional constructs.
  36. (struct coordinate ([x : Integer] [y : Integer]))
  37.  
  38. ; The comparison function, it seems to me, is the beating heart at the center of quadtrees.
  39. ; You just gotta discern which quadrant below a node to categorize a set of coordinates in.
  40. ; Also, exact match/collisions.
  41. (: coordinate-compare (-> coordinate coordinate Quadenum))
  42. (define (coordinate-compare a b)
  43.   (cond [(and (equal? (coordinate-x a) (coordinate-x b))
  44.               (equal? (coordinate-y a) (coordinate-y b))) 'self]
  45.         [(and (<= (coordinate-x a) (coordinate-x b))                 ; Why are some of these -or-equals?
  46.               (< (coordinate-y a) (coordinate-y b))) 'NE]            ; To keep the tree perfectly balanced, as all things should be.
  47.         [(and (> (coordinate-x a) (coordinate-x b))                  ; Otherwise, two quadrants would get all the luck.
  48.               (<= (coordinate-y a) (coordinate-y b))) 'NW]           ; Any bias adds up and unbalances the tree.
  49.         [(and (>= (coordinate-x a) (coordinate-x b))                 ; Therefore, each quadrant owns a cardinal direction of its own.
  50.               (> (coordinate-y a) (coordinate-y b))) 'SW]
  51.         [(and (< (coordinate-x a) (coordinate-x b))
  52.               (>= (coordinate-y a) (coordinate-y b))) 'SE]
  53.         [else (raise "How did you get here that is not how integers work")])) ; I am confident the other options are exhaustive.
  54.  
  55. ; Determine if a particular coordinate pair is a member of a quadtree.
  56. (: quadtree-member? (-> Quadtree coordinate Boolean))
  57. (define (quadtree-member? quadtree coords)
  58.   (cond [(null? quadtree) #f] ; Nulls have no children.
  59.         [else
  60.          (let ([quadrant (coordinate-compare (node-coords quadtree) coords)])
  61.            (cond [(equal? quadrant 'self) #t]
  62.                  [else (quadtree-member? (map-directions quadtree quadrant) coords)]))]))
  63.  
  64. ; A mapping of struct accessor functions to the non-self enumerators returned by coordinate-compare.
  65. (: map-directions (-> node Quadenum Quadtree))
  66. (define (map-directions node quadenum)
  67.   (cond [(equal? quadenum 'NE) (get-node-child node node-NE)]
  68.         [(equal? quadenum 'NW) (get-node-child node node-NW)]
  69.         [(equal? quadenum 'SW) (get-node-child node node-SW)]
  70.         [(equal? quadenum 'SE) (get-node-child node node-SE)]
  71.         [else (raise "Don't call this on yourself!!")])) ; Self should never get mapped to.
  72.  
  73. ; Insertion
  74. ; Given a null Quadtree, return a new Quadtree with null children, containing the coordinates.
  75. ; Given a Quadtree whose coordinates equal those supplied, return the original Quadtree.
  76. ; Given a non-null, non-matching node, recursively traverse to a null child and return a new tree with the coordinates at that location.
  77. (: quadtree-insert (-> Quadtree coordinate node))
  78. (define (quadtree-insert quadtree coords)
  79.   (cond [(null? quadtree) (node null null null null coords)]                                  ; You've landed: returning a new leaf on the tree.
  80.         [(equal? 'self (coordinate-compare (get-node-coords quadtree) coords)) quadtree]      ; This quadtree contains the requested coordinates. Return original quadtree.
  81.         [else
  82.          (let* ([quadrant (coordinate-compare (get-node-coords quadtree) coords)]             ; The quadrant these coordinates belong to on quadtree.
  83.                 [recurse (quadtree-insert (map-directions quadtree quadrant) coords)]         ; Reach down `quadrant' and hand up what's down there.
  84.                 [NE (if (equal? quadrant 'NE) recurse (get-node-child quadtree node-NE))]     ; Ensure correct arity when assembling this node's representation to hand up
  85.                 [NW (if (equal? quadrant 'NW) recurse (get-node-child quadtree node-NW))]     ; the stack. Only one of these will call 'recurse', and that one will
  86.                 [SW (if (equal? quadrant 'SW) recurse (get-node-child quadtree node-SW))]     ; be inserted into the final product in the correct field
  87.                 [SE (if (equal? quadrant 'SE) recurse (get-node-child quadtree node-SE))])
  88.            (node NE NW SW SE (get-node-coords quadtree)))])) ; Here, we package up everything we know about the current Quadtree from this point down and send it up the recursion stack.

=>