Arcadiaen
Arcadiaen

Reputation: 33

How to use maps and filters in Racket

Im having an issue trying to use the map and filter in Racket for the following:

enter image description here

I'm representing a table of data using a struct, and I know I need to give that table to a map function to iterate through, and then filter the non-hardworker entries out for the final list, but I have no idea how to get the info I need out of my struct or how the map and filter should look...

This is what I have:

#lang racket

(struct worker (name work study ent))

(define workers(list
                     (worker '(bill) '(none) '(medium) '(none))
                     (worker '(jill) '(high) '(low) '(medium))
                     (worker '(tim) '(vhigh) '(none) '(vhigh))
                     (worker '(gary) '(medium) '(high) '(medium))
                     (worker '(samantha) '(vlow) '(vlow) '(medium))
                     (worker '(holly) '(vlow) '(low) '(low))
                     (worker '(ryan) '(low) '(low) '(low))
                     (worker '(quin) '(low) '(medium) '(vlow))
                     (worker '(lisa) '(medium) '(vlow) '(high))
                     (worker '(jennifer) '(low) '(low) '(vlow))
                     (worker '(jeff) '(high) '(low) '(high))
                     (worker '(george) '(medium) '(vhigh) '(medium))
                     (worker '(beth) '(none) '(none) '(low))
                     (worker '(maria) '(vlow) '(medium) '(low))
                     (worker '(simon) '(medium) '(high) '(high))
                     ))

(define convert
  (lambda (input)
    (match (input
           ('none 0)
           ('vlow 1)
           ('low 2)
           ('medium 3)
           ('high 4)
           ('vhigh 5)
           )
          )
    )
)

(define (hardworker workers)
  (map(lambda(workers)...

Any help is appreciated.

Upvotes: 0

Views: 1296

Answers (2)

Atharva Shukla
Atharva Shukla

Reputation: 2137

#lang racket
(require rackunit)

Representing Workers

Note that since you're putting '( ... ) (quoted parenthesis) each field is a [Listof Symbol], instead of just symbols by themselves as represented in this answer. Each struct gives rise to "selector functions" which can extract values of fields.

(worker-ent (worker '(quin) '(low) '(medium) '(vlow))) 
; => '(vlow). 

(first '(vlow)) 
; => 'vlow. 

If we were to represent the fields of the struct using just symbols, it would be:

(define workers
  (list   ; name     work    study   ent
   (worker 'bill     'none   'medium 'none)
   (worker 'jill     'high   'low    'medium)
   (worker 'tim      'vhigh  'none   'vhigh)
   (worker 'gary     'medium 'high   'medium)
   (worker 'samantha 'vlow   'vlow   'medium)
   (worker 'holly    'vlow   'low    'low)
   (worker 'ryan     'low    'low    'low)
   (worker 'quin     'low    'medium 'vlow)
   (worker 'lisa     'medium 'vlow   'high)
   (worker 'jennifer 'low    'low    'vlow)
   (worker 'jeff     'high   'low    'high)
   (worker 'george   'medium 'vhigh  'medium)
   (worker 'beth     'none   'none   'low)
   (worker 'maria    'vlow   'medium 'low)
   (worker 'simon    'medium 'high   'high)))

In fact it is wise to write down what each field represent along with their type:

(struct worker (name work study ent) #:transparent)
; A Worker is a (worker Symbol Amount Amount Amount)
; interpretation. name is the name of the worker, work, study and ent are
; the Amount that represents the work, study and entertainment done by the worker. 

; Amount is one of
; - 'none
; - 'vlow
; - 'low
; - 'medium
; - 'high
; - 'vhigh
; interpretation. represents amount of something done. 

The convert function

convert is putting the "Amount" enumeration into an order, however the syntax used for match has problems. Here is a full definition with tests:

; convert : Amount -> Natural
; The ordering number of each Amount.
(define convert
  (lambda (input)
    (match input
      ['none   0]
      ['vlow   1]
      ['low    2]
      ['medium 3]
      ['high   4]
      ['vhigh  5])))

(check-equal? (convert 'none)   0)
(check-equal? (convert 'vlow)   1)
(check-equal? (convert 'low)    2)
(check-equal? (convert 'medium) 3)
(check-equal? (convert 'high)   4)
(check-equal? (convert 'vhigh)  5)

Note 1: We can avoid using match by using index-of as follows:

(define (convert.v2 i)
  (index-of '(none  vlow low medium high vhigh) i))

(check-equal? (convert.v2 'none)   0)
(check-equal? (convert.v2 'vlow)   1)
(check-equal? (convert.v2 'low)    2)
(check-equal? (convert.v2 'medium) 3)
(check-equal? (convert.v2 'high)   4)
(check-equal? (convert.v2 'vhigh)  5)

Predicates on Amount

To break the problem down, we need to know what it means for an Amount to be "at least High" and "no more than Medium" so we make the following predicates:

; at-least-high? : Amount -> Boolean
; is `a` at least High?
(define (at-least-high? a)
  (>= (convert a) 4))

(check-false (at-least-high? 'none))
(check-false (at-least-high? 'vlow))
(check-false (at-least-high? 'low))
(check-false (at-least-high? 'medium))
(check-true  (at-least-high? 'high))
(check-true  (at-least-high? 'vhigh))

and

; no-more-than-medium? : Amount -> Boolean
; is `a` not more than Medium?
(define (no-more-than-medium? a)
  (<= (convert a) 3))

(check-true  (no-more-than-medium? 'none))
(check-true  (no-more-than-medium? 'vlow))
(check-true  (no-more-than-medium? 'low))
(check-true  (no-more-than-medium? 'medium))
(check-false (no-more-than-medium? 'high))
(check-false (no-more-than-medium? 'vhigh))

Note 2: that we can simply check for members of the amount enumeration that are valid for our condition, in which case we wouldn't need convert.

(define (at-least-high?.v2 a)
  (or (symbol=? a 'high) (symbol=? a 'vhigh)))

(define (no-more-than-medium?.v2 a)
  (or (symbol=? a 'none) (symbol=? a 'vlow)
      (symbol=? a 'low) (symbol=? a 'medium))) 

hardworker? predicate

What does it mean for someone to be a hardworker? It's just the conjunction of the two predicates already defined:

; hardworker? : Worker -> Boolean
; Hard workers do at least high amount of work and
; have no more than medium amount of entertainment.
(define (hardworker? w)
  (and (at-least-high? (worker-work w)) 
       (no-more-than-medium? (worker-ent w))))

(check-true  (worker 'bill 'none 'medium 'none))
(check-false (worker 'jill 'high 'low    'medium))

Note 3: If I had your definition of workers, I'd have to wrap (worker-work w) and (worker-ent w) with first to extract the symbol out of the list.

Finally, the workers list can be filtered using hardworker?.

Note 4: We don't need map to iterate over the list, filter recurs over the list behind the scenes.

; hardworker : [List-of Worker] -> [List-of Worker]
; Only keep hard-workers. 
(define (hardworker workers)
  (filter hardworker? workers))

(check-equal? (hardworker workers) (list (worker 'jill 'high 'low 'medium)))
(check-equal? (hardworker (rest (rest workers))) empty)

Smaller version

We can consolidate everything into just one function using a lambda:

(define (hardworker.v2 workers)
  (filter (λ (w) (and (member (worker-work w) '(high vhigh)) 
                      (member (worker-ent w)  '(none vlow low medium))))
          workers))

Upvotes: 3

tjorchrt
tjorchrt

Reputation: 702

#lang racket
(define-struct worker (name work study ent) #:transparent #:mutable)

(define workers-list
  (list
   (make-worker 'bill 'none 'medium 'none)
   (make-worker 'jill 'high 'low 'medium)
   (make-worker 'tim 'vhigh 'none 'vhigh)
   (make-worker 'gary 'medium 'high 'medium)))
; work hard
(filter (lambda (w) (equal? 'high (worker-work w))) workers-list)
; not wrok hard
(filter (lambda (w) (not (equal? 'high (worker-work w)))) workers-list)

Upvotes: 0

Related Questions