Lab 9 Debugging

home work!

image

A famous quote: "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live."

What is debugging? A process of identifying and removing errors from software

What’s the origin of the term "debugging"? In the context of programming, the term "debugging" or "bug" was coined by Admiral Grace Hopper aka "Amazing Grace"- an American computer scientist who developed one of the first compilers for a computer programming language. While she was working at Harvard University, her colleagues couldn’t figure out what was wrong with their programs until they found a moth that was stuck inside the computer which was causing an interference.

Why learn to debug? Unfortunately, many programmers fall into the trap of writing messy code and forget to write purpose statements or good tests. Whether you are writing new code or maintaining someone else’s code, you will most likely have to debug your own code or someone else’s code.

Let’s get started In today’s lab, we will focus on tackling messy code and popular misunderstandings as well as learn about common pitfalls that should be avoided when designing and implementing code.

image

(define-struct student (name id grade))
; A Student is a (make-student String Number Number)
; where name refers to the student's name and
; the id refers to his/her school ID and the grade
; refers to his/her exam grade
 
; Student -> Student
; Checks if the given student is a high-achiever
(define (high-achiever? student)
  (cond [(and (>= (student-grade student) 90) (<= (student-grade student) 100)) true]
        [(< (student-grade student) 90) false]))
 
(check-expect (high-achiever? (make-student "Charlie Brown" 123 74)) false)
(check-expect (high-achiever? (make-student "Lucy van Pelt" 456 95)) true)

Exercise 1 What does student in (define-struct student...) refer to?

Exercise 2 What is Student with a capitalized "S"?

Exercise 3 What does student in (define (high-achiever? student) refer to?

Exercise 4 Can you improve the above code so that the conditional statement is no longer needed?

image

Your peer writes a function, draw-discs, that draws images of discs (circles) onto a scene. Although it functions correctly, the code has terrible style.

(define-struct disc (size color pos))
; A Disc is a (make-disc number string Posn).
; size and color refer to visual properties of the disc
; and pos refers to its position on the scene
 
; [Listof Disc] [Listof Disc] Image -> Image
; Draws the given list of discs onto the given background
(define (draw-discs lod1 lod2 bg)
  (draw-helper10 lod1 (draw-helper10 lod2 bg)))
 
; [Listof Disc] -> [Listof Disc]
(define (draw-helper10 lod bg)
  (cond
    [(cons? lod) (place-image (draw-helper3 (first lod))
                             (posn-x (disc-pos (first lod)))
                             (posn-y (disc-pos (first lod)))
                             (draw-helper10 (rest lod) bg))]
    [(empty? lod) bg]))
 
; Disc -> Image
; Draws the disc
(define (draw-helper3 lod)
  (circle (disc-size lod) "solid" (disc-color lod)))
 
(define d1 (make-disc 5 "red" (make-posn 10 10)))
(define d2 (make-disc 4 "red" (make-posn 20 20)))
(define d3 (make-disc 3 "red" (make-posn 30 30)))
(define d4 (make-disc 6 "blue" (make-posn 10 5)))
(check-expect (draw-discs (list d1 d2 d3) (list d4) (empty-scene 100 100))
              (place-image (circle 5 "solid" "red") 10 10
                           (place-image (circle 4 "solid" "red") 20 20
                                        (place-image (circle 3 "solid" "red") 30 30
                                                     (place-image (circle 6 "solid" "blue") 10 5
                                                                  (empty-scene 100 100))))))

Practice: The test given to you is correct. But the code has terrible function names and input names. Let’s try to make the code more readable

Exercise 5 Fix bad function names and input names (e.g. draw-helperX is a terrible function name, can you improve it?). Remember to also change the function names in the test.

Exercise 6 There are bad signatures and missing purpose statements. Can you fix them?

Exercise 7 Why does the following function blow up when you try to run it on the test? Can you fix it?
; get-disc-sizes: [List-of Disc] -> [List-of Number]
; Returns the sizes of the discs
 
 (define (get-disc-sizes lod)
   (local [(define size
             (disc-size (first lod)))]
     (cond
       [(cons? lod) (cons size  (get-disc-sizes (rest lod)))]
       [(empty? lod) empty])))
 
 (check-expect (get-disc-sizes (list d1 d2 d3 d4)) (list 5 4 3 6))

image

An employee was asked to write a function, place-balls, which is given the position of a red ball and the positions of a blue ball and should produce an image of the red ball and blue ball joined by a black line with a purple ball placed in the middle and a green ball placed 3/4 of the distance between red and blue from the red.

Your boss finds out that this employee was writing terrible code and decides to hand you the job of fixing his mess. Here is his code:

(require 2htdp/image)
(define RED-ball (circle 10 "solid" "red"))
; posn Posn -> images
; Takes 2 posns and places a red ball and a blue ball at two seperate coord with a line running
; between them and a green ball on that line that is 10% closer to the red ball and draws that image
(define (place-balls red blue)
  (place-image RED-ball
               (posn-x red)
               (posn-y red)
               (place-image (circle 10 "solid" "blue") (posn-x blue)
                            (posn-y blue) (place-image (circle 10 "solid" "green")
                                                       (+ (* 0.75 (- (posn-x blue) (posn-x red)))
                                                          (posn-x red))
                                                       (+ (* 0.5 (- (posn-y blue)
                                                                    (posn-y red)))
                                                          (posn-y red))
                                                       (place-image (circle 10 "solid" "purple")
                                                                    (+ (* 0.75 (- (posn-x blue) (posn-x red))) (posn-x red)) (+ (* 0.5 (- (posn-y blue)
                                                                                                                                          (posn-y red))) (posn-y red))
                                                                    (add-line (empty-scene 200 250) (posn-x blue)
                                                                              (posn-y blue) (posn-x red) (posn-y red) "black"))))))

Let’s try to fix his code. Here are some guidelines:

Exercise 8 Write a test for the function so that you are certain about what it should produce. ;; Hint: make a very simple test where the balls are placed ;; on a horizontal line e.g. red is on (0, 50) and blue is on (100, 50).

Exercise 9 Start by reindenting the code (reminder: ctrl+i and cmd+i) then go through the code ;; and format it so that it looks clearer

Exercise 10 Fix the "magic" numbers e.g. .1, 200, 250, 10. Hint: use constants!

Exercise 11 Is the constant name RED-ball appropriate? Can you improve it?

Exercise 12 Can you correct the signature?

Exercise 13 Can you improve the purpose statement?

Exercise 14 Do you see any repetitions in the code? Can you abstract those and make it a helper function?

Exercise 15 Can you fix a silly bug in the code?

image

Your friend has just learned how to make functions local, however, he places local functions in places that make the function confusing to read.

(define-struct outfit (blazer pants shirt))
; An Outfit is a (make-outfit Blazer Pants Shirt))
; in which each outfit consists of matching blazer, pants and shirt
 
(define-struct blazer (size color padded?))
; A Blazer is a (make-blazer Size String Boolean)
; where size and color refers to its appearance and padded?
; refers to whether or not the shoulders contain pads
 
(define-struct pants (size color length))
; A Pants is a (make-pants Number String Number))
; where size, color and length refers to its appearance
 
(define-struct shirt (size color))
; A Shirt is a (make-shirt Number String)
; where size and color refer to its appearance
 
; A Size is one of
; - "S"
; - "M"
; - "L"
 
; Outfit String -> Outfit
; Changes the given outfit to have the given color
(define (mono-colorize ouft color)
  (local [ (define (colorize-blazer b c)
             (make-blazer (blazer-size b)
                          c
                          (blazer-padded? b)))]
    (make-outfit (colorize-blazer (outfit-blazer ouft) color)
                 (local[ (define (colorize-pants p c)
                           (make-pants (pants-size p)  c
                                       (pants-length p)))]
                   (colorize-pants (outfit-pants ouft) color))
                 (make-shirt (shirt-size (outfit-shirt ouft)) color))))
 
(define blazer1 (make-blazer 10 "purple" true))
(define pants1 (make-pants 28 "blue" 100))
(define shirt1 (make-shirt 8 "white"))
(define outfit1 (make-outfit blazer1 pants1 shirt1))
(define blazer2 (make-blazer 10 "orange" true))
(define pants2 (make-pants 28 "orange" 100))
(define shirt2 (make-shirt 8 "orange"))
(define outfit2 (make-outfit blazer2 pants2 shirt2))
(check-expect (mono-colorize outfit2 "orange") outfit2)

Let’s try to understand the code and format it so that it’s more readable. Note: The test given to you is correct so you can use it to check if your fix is still valid.

Exercise 16 What is the difference between the size in the structure blazer and the size in pants and shirt? Hint: Look at the types. Are these types valid?

Exercise 17 Try to format the local functions so that it is more readable ;; Hint: more than one function can go in (local [...])

Exercise 18 The local functions take in an unnecessary input. Which one is it?

image

Some times it may seem tedious to write descriptive function names, but there is a pay off when you make your code clearer. For example, look at this function with vague function and input names:

; [List-of Number] [List-of Number] -> [List-of Number]
; Delete items in los1 that exist in los2
(define (delete-common-bad a b)
  ; Number -> Boolean
  ; Checks of the given number exists does not exist in los2
  (local [(define (hlp y)
            (not (ormap (lambda (x) (= y x)) b)))]
    (filter hlp a)))
 
(check-expect (delete-common-bad (list 1 2 3) (list 5 6 2 1)) (list 3))

The above is quite difficult to read, compare it to this:

; [List-of Number] [List-of Number] -> [List-of Number]
; Delete items in los1 that exist in los2
(define (delete-common-good los1 los2)
  ; Number -> Boolean
  ; Checks of the given number exists does not exist in los2
  (local [(define (is-in-los2? e-los1)
            (not (ormap (lambda (e-los2) (= e-los1 e-los2)) los2)))]
    (filter is-in-los2? los1)))
 
(check-expect (delete-common-good (list 1 2 3) (list 5 6 2 1)) (list 3))

This naming makes it easier to understand where the input is coming from and what the functions are meant to handle. Although it can be good to be specific in your function names as well as input names, it is not good practice when you are trying to create functions that are absrtact E.g. If is-in-los2? were to take in values not from los1, then the input name e-los1 does not make sense and a more generic name will be preferable.

After reading the above example, let’s try to improve the code below.

; [List-of String] [List-of String] -> [List-of String]
; Sums up the number of times that the strings in the first list appears in the second list
 
 (define (sum-occurences los1 los2)
   ; Adds the number of times e is found in los2 to the result
   (local [(define (helper e result)
             (+  (foldr (lambda (es result) (if (string=? es e) (+ 1 result) (+ 0 result)))
                        0
                        los2)
                 result))]
     (foldr helper 0 los1)))
 
(check-expect (sum-occurences (list "a" "b" "c") (list "a" "d" "b" "a" "c" "b" "b")) (+ 2 3 1))

Note: this test is valid and you can use to to check if your improved code works with it.

Exercise 19 Can you replace "helper" with a better name?

Exercise 20 Write a signature for the local function

Exercise 21 Improve the input names: e and es

Exercise 22 Improve the input name(s): result. Hint - Say you have the function:

(define (foo x) ((lambda (x) x) 5))

What does (foo 8) return? Why does it return 5 and not 8?