consuming lists racket - scheme

(define-struct restaurant (name address lod))
(define-struct dish (name main-ingredient price))
(define lod
(list
(make-dish "BNC" "pepper" 15.00)
(make-dish "BBQ" "sauce" 20.00)
(make-dish "BAW" "vanilla" 18.00)
(make-dish "ACA" "tomato" 15.00)
(make-dish "NAM" "onions" 25.00)
(make-dish "PLA" "milk" 34.00)))
(define lod1
(list
(make-dish "AHSH" "carrots" 23.00)
(make-dish "PROA" "garlic" 21.00)
(make-dish "MENE" "eggs" 16.00)
(make-dish "SAYB" "water" 11.00)
(make-dish "WAFA" "chicken" 22.00)
(make-dish "LDAFK" "chocolate" 10.00)))
(define Sky-Dragon (make-restaurant "Sky Dragon" "23 Cutoiut, NY" lod))
(define Tomas (make-restaurant "Tomas" "34 Rossi, CA" lod1))
(define restaurant1 Sky-Dragon)
(define restaurant2 Tomas)
I would like to know how would you write a function that consumes a restaurant and produces the total number of dishes the restaurant has. Like I take restaurant1, I get 6 dishes. Hope I am doing this right.

It's simple, just use the accessor procedure to retrieve the list of dishes of a restaurant, and then the built-in procedure to determine the size (or "length") of that list:
(define (number-of-dishes restaurant)
(<???> ; how do we determine the size of a list?
(<???> restaurant))) ; how do we access a restaurant's list of dishes?
I'll let you figure out the details, but remember - whenever you define a new struct using define-struct, an accessor procedure is automatically created for each of its fields. For example, the price of a dish is obtained by calling dish-price on a dish: (dish-price dish).

Related

Filtering in Clojure

I have a map like this
(def invoice
{:productId ["001" "002" "003" "004" "005" "006" "007" "008" "009" "010"],
:price ["50" "60" "70" "50" "40" "45" "55" "90" "50" "70"],
:quantity ["0" "0" "1" "2" "0" "0" "0" "0" "0" "1"]})
how can i filter so it only show product id where the quantity is 1 or more ?
i already tried to make like this
(filter (> (invoice :quantity %) 1) (map list (invoice :price) (invoice :quantity) (invoice :productid))
but it doesn't work
You need to create a function to pass as the first argument to filter. Secondly, you need to parse the quantities before you do the comparison to 1, which you can do using read-string:
(filter #(> (read-string (second %)) 1) (map list (invoice :price) (invoice :quantity) (invoice :productId)))
first step would be construct a pair of Product ID and quantity:
(map vector (invoice :quantity) (invoice :productId))
;; ["0" "001"] .... first element would be quantity and second is productiID
second step would be filter out which quantity is greater than 0, here I use (Integer. xx) to conver the quantity to a number.
(filter #(> (Integer. (first %)) 0) (map vector (invoice :quantity) (invoice :productId)))
Usually, data is easier to deal with when it's a little more 'normal' so you don't have to worry about indices like in your sample. I'd reccomend storing it as a seq of ({:id x :price y :quant z} ... ).
This function does that:
(defn invoices [invoice]
(map (fn [id price quant]
{:id id
:price price
:quant quant}) (:prouctId invoice)
(:price invoice)
(:quantity invoice))
Then from there you can simply
(->> invoice
invoices
(remove #(= "0" (:price %)) ;; remove "0" prices
(map :id))) ;; get ids
assuming that your dataset doesn't have negative quantities.

lisp iterate through list

I just started GIMP script-fu scripting yesterday for a script I need but I have no idea how automate the adding of the layers in the local variables and calling the function for adding the layers to the image. I tried iterating over lists but failed gracefully. Can someone push me in the right direction?
(define (script-fu-gifslot inText inFont inFontSize inTextColor)
(let*
((theImageWidth 700) ; define our local variables
(theImageHeight 100) ; create a new image:
(theImage)
(layerX)
(inOffset)
(theImage (car (gimp-image-new theImageWidth theImageHeight RGB)))
(theText) ;a declaration for the text
(theLayer (car (gimp-layer-new theImage theImageWidth theImageHeight RGB-IMAGE "layer 1" 100 NORMAL)))
(theLayer2 (car (gimp-layer-new theImage theImageWidth theImageHeight RGB-IMAGE "layer 2" 100 NORMAL)))
(theLayer3 (car (gimp-layer-new theImage theImageWidth theImageHeight RGB-IMAGE "layer 3" 100 NORMAL)))
(theLayer4 (car (gimp-layer-new theImage theImageWidth theImageHeight RGB-IMAGE "layer 4" 100 NORMAL))))
(define (yannis-add-new-layer layerX inOffset)
(gimp-image-add-layer theImage layerX 0)
(gimp-context-set-background '(255 255 255))
(gimp-context-set-foreground inTextColor)
(gimp-drawable-fill layerX BACKGROUND-FILL)
(set! theText (car (gimp-text-fontname theImage layerX 0 0 inText 0 TRUE inFontSize PIXELS "Sans")))
(set! theImageHeight (car (gimp-drawable-height theText)))
(gimp-layer-resize layerX theImageWidth theImageHeight 0 0)
(gimp-layer-set-offsets theText 0 (- 0 inOffset)))
(yannis-add-new-layer theLayer 0)
(yannis-add-new-layer theLayer2 10)
(yannis-add-new-layer theLayer3 20)
(yannis-add-new-layer theLayer4 30)
(gimp-display-new theImage)
(list theImage layerX theText)))
(script-fu-register
"script-fu-gifslot"
"_GGGIF WORD Slotmachine..."
"Creates an image with a user specified text string."
"Yannis De Cleene <yannisdcl#gmail.com>"
"Yannis De Cleene"
"July, 2014"
""
SF-TEXT "Text" "PASTE\nMULTILINE TEXT\nIN HERE"
SF-FONT "Font" "Sans"
SF-ADJUSTMENT "Font size (pixels)" '(100 2 1000 1 10 0 1)
SF-COLOR "Color" '(0 0 0))
(script-fu-menu-register "script-fu-gifslot" "<Image>/File/Create")
Use either map or for-each or do.
If you want to add 1 to a list you could use (map 1+ '(1 2 3)). Or if you want to define the function on the fly, use lambda: (map (lambda (x) (+ 1 x)) '(1 2 3))
If you want a more imperative style use do. You can read about it here.
You can search for functions (and other bound symbols) using the function apropos. e.j. (apropos "for") (Apparently not in gimp to my surpise)
However I sense your problem is larger than just iterate over a list. For example does it make sense to define the function over the let? why are you not explicitly passing the image to the add-new-layer-function?
To make your code more readable I would drop the 'the', use '-' instead of camelCase and don't leave one parentesis in each line. it hurts readability. I can see however it is not your fault as that style is used in the gimp's tutorial. e.j. theImage becomes image and TheLayer1 becomes layer-1.
If you want to learn more about scheme you should download racket and read how to design programs which is a good introduction to programming.

Sort list of 3 elements by particular element, then display

In my program the user will enter 3 elements for each student:
ID
Name
Grade
These are put into a list which will look like (a scheme list)
( (001 "Bob" 80) (002 "Sam" 85) (003 "Aaron" 94) etc . . .)
If the user chooses to sort by name, I'd then like the list to display the info like:
No.1: ID=003, Name=’’Aaron’’, Grade=94
No.2: ID=001, Name=’’Bob’’, Grade=80
No.3: ID=002, Name=’’Sam’’, Grade=85
I finished the function to generate the list but i'm struggling with sorting the list and displaying. any help would be appreciated, thanks
An implementation of sort in Scheme is found in Wikibooks. It is called mergesort. At its core, it assumes that you can use < to compare two elements of the list for sorting purposes.
You can modify mergesort to take an additional argument, less-proc, and use it wherever < is used.
Then you can call mergesort with:
(mergesort lst (lambda (a b) (string<? (cadr a) (cadr b))))
Check your interpreter's documentation for a sorting procedure. For example in Racket you can sort the following list:
(define lst '((001 "Bob" 80) (002 "Sam" 85) (003 "Aaron" 94)))
Ascending, using the name:
(sort lst #:key second string<?)
=> '((3 "Aaron" 94) (1 "Bob" 80) (2 "Sam" 85))
Descending, using the grade:
(sort lst #:key third >)
=> '((3 "Aaron" 94) (2 "Sam" 85) (1 "Bob" 80))
… You get the idea. For the second part of the question, once again refer to your interpreter's documentation. In Racket printf comes in handy - for instance, for printing the records after sorting them by name:
(for ([i (in-naturals 1)]
[record (sort lst #:key second string<?)])
(printf "No.~a: ID=~a, Name=’’~a’’, Grade=~a~n"
i
(~a (first record) #:min-width 3 #:align 'right #:left-pad-string "0")
(second record)
(third record)))
=> No.1: ID=003, Name=’’Aaron’’, Grade=94
No.2: ID=001, Name=’’Bob’’, Grade=80
No.3: ID=002, Name=’’Sam’’, Grade=85

developing simple functions

(define-struct animal (name species age breakfasthour dinnerhour))
(define-struct attendant (name a1 a2 a3))
(define gorilla (make-animal "Koko" "Gorilla" 4 "8" "10"))
(define bat (make-animal "Bruce" "Bat" 1 "23" "5"))
(define mandrill (make-animal "Manny" "Mandrill" 5 "8" "7"))
(define crocodile (make-animal "Swampy" "Crocodile" 1 "10" "8"))
(define ocelot (make-animal "Ozzy" "Ocelot" 7 "7" "17"))
(define capybara (make-animal "Capy" "Capybara" 4 "6" "8"))
(define potto (make-animal "Spot" "Potto" 2 "2" "6"))
(define tapir (make-animal "Stripey" "Tapir" 3 "10" "6"))
(define vulture (make-animal "Beaky" "Vulture" 10 "9" "6"))
(define attendant1 (make-attendant "Dave" gorilla bat mandrill))
(define attendant2 (make-attendant "John" crocodile ocelot capybara))
(define attendant3 (make-attendant "Joe" potto tapir vulture))
I need a function that takes an animal and returns whether its mealtime, if i take gorilla, then dinner time would be at 10. This is what I've done. ignore the quotes on the numbers above.
(define (meal-time? e1 e2)
(string=? (animal-species e1)
(animal-dinnerhour e2)))
it runs, but wnt give me an output. any reason why it wont give me an output?
edit- (meal-time? gorilla 10)tells me it expects an animal, but given 10.
Your meal-time? function takes two animals as arguments (because you use animal- accessor functions on both arguments), but you call it with an animal and a number. So you get an error message telling you that the second argument should be an animal.
If you call your function with two animals as arguments, you'll get no error any more. You'll get #f. What your function does is: it checks whether the species of the first animal is equal to the the dinner hour of the second animal. Since there is no species whose name is a number, that will never be true.

data definitions in scheme?

I've been working on this today and what I am left to do is develop a function that takes an attendant and returns the total age of the animals the attendant has been assigned to. So if I take for example attendant Dave, I would get 10. Not sure where to start. How would you add the ages up?
(define-struct animal (name species age breakfasthour dinnerhour))
(define-struct attendant (name a1 a2 a3))
(define gorilla (make-animal "Koko" "Gorilla" "4" "8" "10"))
(define bat (make-animal "Bruce" "Bat" "1" "23" "5"))
(define mandrill (make-animal "Manny" "Mandrill" "5" "8" "7"))
(define crocodile (make-animal "Swampy" "Crocodile" "1" "10" "8"))
(define ocelot (make-animal "Ozzy" "Ocelot" "7" "7" "17"))
(define capybara (make-animal "Capy" "Capybara" "4" "6" "8"))
(define potto (make-animal "Spot" "Potto" "2" "2" "6"))
(define tapir (make-animal "Stripey" "Tapir" "3" "10" "6"))
(define vulture (make-animal "Beaky" "Vulture" "10" "9" "6"))
(define attendant1 (make-attendant "Dave" gorilla bat mandrill))
(define attendant2 (make-attendant "John" crocodile ocelot capybara))
(define attendant3 (make-attendant "Joe" potto tapir vulture))
#;(define (meal-time? e1 e2)
(string=? (animal-species e1)
(animal-dinnerhour e2)))
#;(define (animal-template s...)
(...(animal-name s)...
(animal-species s)...
(animal-age s)...
(animal-breakfasthour s)...
(animal-dinnerhour s)...))
#;(define (attendant-template s...)
(...(attendant-name s)...
(attendant-s1 s)...
(attendant-s2 s)...
(attendant-s3 s)...))
Simply use the accessor procedures for each type:
(define (animals-age att)
(+ (animal-age (attendant-a1 att))
(animal-age (attendant-a2 att))
(animal-age (attendant-a3 att))))
Clearly, this works only if the age is a number, in your current code is a string (why?), please consider representing ages as numbers, it makes more sense.

Resources