schemeracketabstraction

Say we have an abstract data type for cities. A city has a name, a latitude coordinate, and a longitude coordinate


Our data abstraction has one constructor:

(make-city name lat lon)

Creates a city object with the given name, latitude, and longitude. We also have the following selectors in order to get the information for each city:

(get-name city)   ;; Returns the city's name
(get-lat city)    ;; Returns the city's latitude
(get-lon city)    ;; Returns the city's longitude

My code:

(define (make-city name lat lon)
  (cons name (cons lat (cons lon null))))

(define (get-name make-city)
  (car make-city))

(define (get-lat make-city)
  (car (cdr make-city)))

(define (get-lon make-city)
  (cdr (cdr make-city)))

(define berkeley (make-city 'Berkeley 122 37))
(get-name berkeley)    'Berkeley
(get-lat berkeley)     122
(get-lon berkeley)     '(37)

The problem is that the result of (get-lon berkeley) is '(37) but i want the output to be 37, what's wrong with my code?


Solution

  • If you're using Racket, you should use a structure instead of a list to represent the city. The default function names created by struct don't match yours, so you can make aliases (Or use the struct-generated ones directly):

    (struct city (name lat lon)
      #:transparent
      #:extra-constructor-name make-city)
    (define get-name city-name)
    (define get-lat city-lat)
    (define get-lon city-lon)
    

    That gives you more efficient storage space and access time compared to a list, gives you city? to easily test if a value is a city, and the only way get-lon will return a list is if you create the city with a list instead of a number for the longitude (And there are ways to prevent that mistake, too, like a #:guard clause or struct contracts)

    If your list-based layout is a requirement of an assignment and can't be changed, though, you might consider list access functions like first. Easier to remember than figuring the correct chain of cars and cdrs.