Search and add to a list in Scheme - list

I want to search for an element in a list, like this one (it's a list of lists)
(name1 (name2 (name3 name4) (name5 (name6))) (name7 (name8 name9)) (name10 (name11 name12)) (name13))
and when I find that element I want to add to it.
Like search for name10 and then add a new name to the name11 and name12 group.
Any help is much appreciated!
(define (adder name2badded indexname treenode)
(display treenode)
(newline)
(cond
((null? treenode)#f) ;"Tree is null"))
((pair? treenode)
(if (adder name2badded indexname (car treenode))
(display "Gotcha!")
(adder name2badded indexname (cdr treenode))
)
) ;END pair?
(else
(eq? indexname treenode)
);END else
);END Cond
)
This is what I have so far, it will find the spot where I want to add it but I can't get to adding to that spot.

You must reconstruct the new tree on your way back from the found point, after you've added your new value there. So you must stop one level above where you're stopping now: at (name old-value ...) instead of at name. This means you must test for equality with (car treenode), not treenode itself. This way you'll be able to construct a new association group as
(cons (car treenode) (cons val2add (cdr treenode)))
and you need to alter your recursion structure to use this new updated assoc group instead of the old one, to reconstruct the whole tree on your way back:
(define (add-into val2add name tree)
(if (pair? tree)
(if (eqv? name (car tree))
(cons name (cons val2add (cdr tree))) ; found!
(cons (add-into val2add name (car tree)) ; it's in CAR or in CDR,
(add-into val2add name (cdr tree)))) ; or maybe in both?
tree))
But if your tree were actually an assoc list and you were allowed to use surgical routines, you could just use
(define (add-into! val2add name als)
(cond ((assv name als) =>
(lambda (a)
(set-cdr! a (cons val2add (cdr a)))))))

Related

Scheme function to take a list and an atom as parameters and returns index of the first location where atom occurs

I'm very new to Scheme and am working on a problem defined as follows:
Write a scheme function find-loc which takes two parameters, a list lst and an atom atm, and returns the index of the first location where atm occurs in the list.
The location index is 1-relative. If atm does not occur in the list, the function returns n + 1, where n is the length of the list.
what I've got so far is:
(define (find-loc list atm)
(if (not(list? list))
0
(cond
((null? list)
1)
((eq? (car list)atm)
1))
(else (+ (find-loc (cdr list atm) 1) )))
I feel that I'm close but am a bit stuck. My main questions are as follows:
how do I define what the atom is?
why am I getting the following error? "define: expected only one expression for the function body, but found 1 extra part"
An atom is defined by this predicate:
(define (atom? x)
(and (not (null? x))
(not (pair? x))))
The error you're receiving is because the syntax of the if expression is incorrect - it must have two parts after the condition (consequent and alternative), and you wrote three. A few comments:
It's better to use a cond when testing for multiple conditions
Don't name a variable as list, it clashes with a built-in procedure of the same name
Better use equal? for testing equality, it's more general
We should use our new and shiny atom? predicate, as per the requirements - even though the null? check is redundant at this point
This is what I mean:
(define (find-loc lst atm)
(cond ((not (list? lst)) 0)
((null? lst) 1)
((and (atom? (car lst)) (equal? (car lst) atm)) 1)
(else (+ 1 (find-loc (cdr lst) atm)))))

scheme recursive function list overrides

I'm trying to do a recursive function that gets a list of string-int pairs + a string named prefix, and using function named "starts-with" it sums up all the ints whose begininng match the prefix.
Problem is, I can never get the list to go forward, it gets stuck at the beginning and then program crashes.
(define (sum-of-pairs-start-with prefix ls)
( let*( (prefix2 (string->list prefix))
(str2 (string->list (car (car ls)))))
(cond((null? str2) 0)
( (starts-with prefix (car(car ls)))
(+ cdr(car ls) (sum-of-pairs-start-with prefix (cdr ls))) )
(else sum-of-pairs-start-with prefix (cdr ls))) ) )
I work with input:
(sum-of-pairs-start-with "a" (list (cons "a" 1) (cons "b" 2) (cons "aa" 33) (cons "ca" 4))) ;; =34
but once i get to the second pair in the list ("b" 2) it goes to the else condition as expected, but then ls gets back up one line to origin (with the previous value) instead of going forward to next value ("aa" 33).
I 'm new to scheme and I dont get why that happens, it's frustrating
You just have to call the starts-with procedure that we previously defined (let it take care of converting the strings to char lists), and fix all of the syntax problems:
(define (sum-of-pairs-start-with prefix ls)
(cond ((null? ls) 0)
((starts-with prefix (car (car ls)))
(+ (cdr (car ls)) (sum-of-pairs-start-with prefix (cdr ls))))
(else (sum-of-pairs-start-with prefix (cdr ls)))))
Once again, you're having trouble invoking the procedures. For example, these snippets are wrong:
cdr(car ls)
(else sum-of-pairs-start-with prefix (cdr ls))
Please, grab a book on Scheme and practice the basic syntax, remember that procedures are not called like this: f(x), the correct way is (f x). Also, pay attention to the correct way to indent the code, it'll be easier to find the errors if you follow the conventions.
Following is a solution using higher functions:
(define (sum-of-pairs-start-with prefix ls)
(apply +
(map cdr
(filter (λ (x) (starts-with prefix (car x)))
ls))))
It filters out those sublists which have prefix in first item, then gets second item (cdr) from each of those sublists and finally applies add function to all of them.

Check if the item in list or sub-list

I want to use the check function to check if the item is in the list or the sub-list.
But the error really confuse me. Can someone tell me what's wrong with my code?
(define check
(lambda(item lis)
(cond((null? lis) #f)
(else(if(pair? (car lis))
(if(check item (car lis)) #t (check item (cdr lis)))
(if(equal? item (car list)) #t (check item (cdr lis))))))))
> (check 'a '(a b))
. . car: contract violation
expected: pair?
given: #<procedure:list>
You have a typo in here:
(equal? item (car list))
It should be:
(equal? item (car lis))
Notice that list is a procedure, and the parameter in your code is called lis.

easy scheme question

how can I find a list from given database??
Database:
(10 math phys)
(11 math chem)
(13 bio chem)
(15 geo phys)
I want to implement time which shows the first and second exams in a list.
>(time ’10)
(list ‘math ‘phys)
>(time ’19)
empty
And I want to implement secondExams which returns the time of second exams.
>(secondExams ‘phys)
(list ‘10 ’15)
>(secondExams ‘chem)
(list ’11 ’13)
thank you
First, let's clarify your database format. Make it a list of lists:
(define database
'((10 math phys)
(11 math chem)
(13 bio chem)
(15 geo phys)))
Then time just needs to step through your list recursively, and compare the first element of the sub-list with your target value. If we find no matching value, we'll return the empty list. We'll do this in a helper procedure to match the functionality you're precisely seeking.
(define time-ish
(lambda (target lst)
(cond ((null? lst) lst)
((eq? target (caar lst)) (cdar lst))
(else
(time-ish target (cdr lst))))))
(define (time lookin-for)
(time-ish lookin-for database))
Then we can do something very similar for secondExams. Except we'll recursively build a list of matches this time.
(define exam-helper
(lambda (target lst)
(cond ((null? lst) lst)
((eq? target (third (car lst))) (cons (first (car lst))
(exam-helper target (cdr lst))))
(else
(exam-helper target (cdr lst))))))
(define (secondExams lookin-for)
(exam-helper lookin-for database))
I haven't tested this code, but I'm pretty sure it'll work for you.

Finding element in list in Scheme

i'm very beginner of Scheme and try to figure out how it is functioning; so i want to write a basic code:
first of all i have a definition set: zipcode (ZIPCODE CITY STATE)
(define zipcodes '(
(96774 ookala hawaii)
(90001 losangeles california)
(90263 malibu california)
(10044 newyork newyork)
))
i try to write a function that input is zipcode and return city and state name for example:
>(find '10044)
(list 'newyork 'newyork)
>(find '99999)
empty because there is not zipcode like that.
Thanks a lot...
I'm not allowed to us LET function
Use assoc
> (assoc 90001 zipcodes)
(90001 losangeles california)
> (cdr (assoc 90001 zipcodes))
(losangeles california)
(define filter
(lambda (proc lst)
(cond ((null? lst) '())
((proc (car lst)) (cons (car lst) (filter proc (cdr lst))))
(else
(filter proc (cdr lst))))))
(define find-zip
(lambda (zip lst)
(define match (filter (lambda (item) (= zip (car item))) lst))
(if (null? match)
'()
(cdar match))))
(define (find zip)
(find-zip zip zipcode))
I think that will work for you. Filter will apply its first argument (a procedure) to each item in its second argument, which needs to be a list. The first argument needs to return a boolean value for each item it's passed. Filter will then return a list with all of the items that returned true when the first argument was applied. Otherwise, it returns an empty list.
In this case, each item in the list you're passing is itself a list of 3 items, so it compares the first item in that list with the zip code you're looking for. If it's a match, it returns true. So if the zip code is in the list, it will return the three item sub-list. Then we check to see if we got an empty list, if so, then we return an empty list. Otherwise, we take the cdr of the car to get your desired city and state.
Well so you can basically just filter the list for the zip code, I have sketched some code below (I'd write it differently, except I don't know what you have available outside of what is defined in RnRS).
(define find-zip
(lambda (zip codelist)
(if (empty? codelist) empty
(if (= zip (car (car codelist)) (list (cadr (car codelist)) (caddr (car codelist)))
(find-zip zip (cdr codelist))))))
It would probably be better should you use a let, and I think most implementations have a filter function that let you do this better.