Search code examples
racketcomputer-science

How to find a point within a square in racket?


Currently, I am struggling with figuring out a function that can determine if a point is in a square or not given the side length and top left corner of the square.

; tl is a Posn, giving the top-left corner of the square
; side is a Number, giving the side length of the square
(define-struct SQ (tl side))
(define z (make-SQ tl side))
(define x (+ (posn-x SQ-tl z) (SQ-side z)))
(define y (+ (posn-y SQ-tl z) (SQ-side z)))
(define a (make-posn x y))
; Posn, SQ -> Boolean
(define (point-in-square? P S)  (and (<= (posn-x P) (posn-x a) S)
                                     (>= (posn-x P) (posn-x (SQ-tl z)) S)
                                     (<= (posn-y P) (posn-y a) S)
                                     (>= (posn-y P) (posn-y (SQ-tl z)) S)))

I receive the error tl: undefined; cannot reference undefined identifier How would I be able to resolve this error?


Solution

  • The initial problems resulted in errors such as: tl: undefined; cannot reference undefined identifier. In the call to construct a square z, the constructor make-SQ was called with arguments tl and side; but no values had been assigned to these identifiers yet. It seems that OP was using them because those were the slot names; constructors need appropriate values to assign to slots. The solution here was just to recognize that construction of an actual SQ instance requires concrete values.

    Getting over the first hurdle made it clear that something was amiss with the definition of the point-in-square? predicate. The definition was attempting to use the globally defined z square and a point. But this procedure also takes P and S arguments, which are a point and a square, respectively. This is all the information that is needed to solve the problem.

    Having worked through the issues with OP in the comments, OP has solved the problems to make the code work. Here is an alternate solution that uses let* to make the code a little nicer to read and think about:

    #lang racket
    
    (define-struct posn (x y))
    
    (define-struct SQ (tl side))
    
    (define (point-in-square? P S)
      (let* ((top-left (SQ-tl S))
             (side-length (SQ-side S))
             (left (posn-x top-left))
             (right (+ left side-length))
             (top (posn-y top-left))
             (bottom (+ top side-length)))
        (and (<= (posn-x P) right)
             (>= (posn-x P) left)
             (<= (posn-y P) bottom)
             (>= (posn-y P) top))))
    

    And a sample REPL interaction:

    point-in-square.rkt> (define test-square (make-SQ (make-posn 4 2) 1))
    point-in-square.rkt> (define p1 (make-posn 5 3))  ; in square
    point-in-square.rkt> (define p2 (make-posn 5 2))  ; on edge of square
    point-in-square.rkt> (define p3 (make-posn 5 1))  ; not in square
    point-in-square.rkt> (point-in-square? p1 test-square)
    #t
    point-in-square.rkt> (point-in-square? p2 test-square)
    #t
    point-in-square.rkt> (point-in-square? p3 test-square)
    #f
    

    Note that let is useful for binding values to be used in a computation to meaningful names; in this case, let* is handy because it lets us use values which were previously bound in the same let* form in subsequent bindings. An alternative would be to nest let forms so that inner lets could use outer bindings.