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

75 Views Asked by At

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?

3

There are 3 best solutions below

0
Shawn On BEST ANSWER

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.

0
ignis volens On

With your definitions, (make-city 'Berkeley 122 37) produces this structure:

Berkeley

Each node is a pair, and in this structure:

  • car picks the thing the left-hand arrow points at (going down the way this has been drawn);
  • cdr picks the thing the right hand arrow points at.

So (cdr (cdr (make-city 'Berkeley 122 37))) follows two right-pointing arrows to return this object:

Broken

Which prints as (37).

I think it should be clear by looking at these diagrams how you need to fix this problem. There are at least two ways: one is better than the other.

(For added points: why are representations like this not good in general?)

0
Sylwester On

Imagine you are making an object with two values. You could just cons them so that (make-object a b) does (cons a b). Then object-a would be car and object-b would be cdr. This is the most efficient.

Your solution uses proper list and the proper list version of the two value object is (list a b) or (cons a (cons b '())) which makes the first accessor the same, but the last would be cadr instead of cdr and there is always an empty list that is never used.

You can solve this either by making your object more efficient like my first example, or by keeping your solution and using the proper accessor. Your choice!