I am working my first project in scheme and have come across an issue. In part of my requirements, I am required to append all top-level sublists
(e.g. '((1 2)(3 4 (5 6))) -> (1 2 3 4 (5 6)) and '((1 2 3)(4 5)) -> (1 2 3 4 5)
I've managed to get it working down to a single list, but this flattens all levels:
(cond
((null? lst)
lst)
((list? lst)
(append2(append-subs(car lst))(append-subs(cdr lst))))
(else
(cons lst '())))
Variations of this (eg. (else lst) run the error "object 6, passed as first arg to cdr, is not correct type". Another method I attempted is as follows:
(cond
((null? lst)
lst)
((>= (len (cdr lst)) 0)
(append2(append-subs(car (list lst)))(append-subs(cdr (list lst)))))
(else
lst)
Which infinitely loops. I'm at a bit of a stand still, so any help would be greatly appreciated. (Note: Use of functions other than those used here is forbidden. Limited to list, list?, if, cond, null? ...)
Your list '(e1 e2 e3) would be like this:
(cons e1 (cons e2 (cons e3 '())))
or if you like dotted notation:
'(e1 . (e2 . (e3 . ())))
Where en is eiter #f or #t for (list? en) Your assignment is to cons en onto the recursion with the same level while with a list you need to append the two.
Here is a general idea how to implement it with level as an input parameter:
;;; flatten a list a certain levels
;;; level of zero is identity
(define (flatten-level level lst)
(cond ((or (zero? level)
(null? lst))
lst)
;; pair? is faster but will fall through for dotted
((list? (car lst))
(append (flatten-level <??> <??>)
(flatten-level <??> <??>)))
(else
(cons <??>
(flatten-level <??> <??>)))))
(flatten-level 0 '((1 2)(3 (((4 . 3))) (5 (6))) . 7))
; ==> ((1 2) (3 (((4 . 3))) (5 (6))) . 7) (aka identity)
(flatten-level 1 '((1 2)(3 (((4 . 3))) (5 (6))) . 7))
; ==> (1 2 3 (((4 . 3))) (5 (6)) . 7)
(flatten-level 99 '((1 2)(3 (((4 . 3))) (5 (6))) . 7))
; ==> (1 2 3 (4 . 3) 5 6 . 7)
How about appending all elements of the top list:
(define (flatten-top-level lst)
(apply append lst))
Which is practically the definition of append*
If '(a (b) (c f)) is a valid input, (first element not a list) then you can try:
(define (flatten-top-level lst)
(apply append
(map (lambda (e) (if (list? e)
e
(list e))) ;make a list from the non-list element
lst)))
2nd option: Fold it!
(define (flatten-top-level lst)
(foldr append '() lst))
For a list (a b c d) where a, b, c, d are sub-lists; it is equal to:
(append a (append b (append c (append d '()))))
Extra: this is tail recurssive and therefore runs in linear time :)
Related
I started learning Scheme today, and wanted to write a function that would reverse a list (only surface level, nested lists stay in the same order).
Heres the function I made:
(define reverse (lambda (lst)
(if (> (length lst) 0)
(cons (reverse(cdr lst)) (car lst))
'()
)))
I thought it would just continuously add the values before everything after it so in the end it's reversed, but when doing (reverse '(5 12 31 7 98 13)), I should get (13 98 7 31 12 5), but instead I get '((((((() . 13) . 98) . 7) . 31) . 12) . 5). How come it's adding periods and parenthesis instead of adding the number to the list?
(list x y ..... z) list structure is constructed like this in Scheme:
(cons x
(cons y
…
…
(cons z '())))
if you use append instead of cons and put your (car lst) into a list of its own, your code will work:
(define reverse
(lambda (lst)
(if (> (length lst) 0)
;;(cons (reverse (cdr lst)) (car lst) )
(append (reverse (cdr lst)) (list (car lst)))
'()
)))
The list (13 98 7 31 12 5) is a visualization of the pairs (13 . (98 . (7 . (31 . (12 . (5 . ())))))). If the cdr is a pair or the empty list the dot and one set of parens is omitted. The code to create this structure is (cons 13 (cons 98 (cons 7 (cons 31 (cons 12 (cons 5 '())))))) while the structure your function creates is (cons (cons (cons (cons (cons (cons 13 '()) 98) 7) 31) 12) 5).
When iterating a list you always do it in order. eg. (1 2 3) is quite difficult to do in reverse. However when creating lists you always do them from end to beginning. eg. (cons 1 (cons 2 (cons 3 '()))) will have to evaluate the cons with 3 before the one with 2 etc. This can be used for a very efficent reverse using an accumulator:
(define (reverse lst acc)
(if (null? lst)
acc
(reverse (cdr lst)
(cons (car lst) acc))))
So imagine calling this (reverse '(1 2 3) '()):
(reverse '(1 2 3) '()) ; =>
(reverse '(2 3) (cons 1 '())) ; ==>
(reverse '(3) (cons 2 '(1))) ; ==>
(reverse '() (cons 3 '(2 1))) ; ==>
'(3 2 1)
I defined a function called zip which took two lists as parameters and returned a list of pairs.
(define (zip list1 list2)
(if (null? list1)
'()
(cons (list (cons (car list1) (car list2)))
(zip (cdr list1) (cdr list2)))))
(zip (list 1 3 5) (list 2 4 6))
> (((1 . 2)) ((3 . 4)) ((5 . 6)))
Now I'm basically having trouble writing the inverse function of this. This is what I have so far. This function needs to output a list of two lists. I only attempted at making the first of the two lists to make it easier for myself but the output is not what I want.
(define (unzip u-list)
(if (null? u-list)
'()
(list (car (car (car u-list))) (unzip(cdr u-list)))))
(unzip (zip (list 1 3 5) (list 2 4 6)))
> (1 (3 (5 ())))
Any help would be appreciated...
I believe there's a problem with your implementation of zip, did you notice that you're returning a list of single-element lists of pairs? returning a list of pairs makes more sense:
(define (zip lst1 lst2)
(if (null? lst1)
'()
(cons (cons (car lst1) (car lst2))
(zip (cdr lst1) (cdr lst2)))))
Or even better, let's use the map higher-order function for a shorter, more idiomatic solution:
(define (zip lst1 lst2)
(map cons lst1 lst2))
Regarding unzip: it's easier if we split the problem in parts - let's get the first element of each pair, then the second element of each pair, and finally build a list with the answer. Try this:
(define (unzip lst)
(define (firsts lst)
(if (null? lst)
'()
(cons (caar lst)
(firsts (cdr lst)))))
(define (seconds lst)
(if (null? lst)
'()
(cons (cdar lst)
(seconds (cdr lst)))))
(list (firsts lst) (seconds lst)))
But once again, we're reinventing the wheel. Let's just use built-in functions to write a simpler answer:
(define (unzip lst)
(list (map car lst) (map cdr lst)))
Anyway, now unzip is the inverse of zip:
(zip '(1 3 5) '(2 4 6))
=> '((1 . 2) (3 . 4) (5 . 6))
(unzip '((1 . 2) (3 . 4) (5 . 6)))
=> '((1 3 5) (2 4 6))
As you make it with pairs it's somewhat more difficult but not much:
(define (zip-pair a b)
(map cons a b))
(define (unzip-pair zipped-pair)
(list (map car zipped-pair)
(map cdr zipped-pair)))
zip is usually implemented with apply and map and takes list and produce list of lists, like this:
(define (zip . lists)
(apply map list lists))
(zip '(1 2 3) '(a b c)) ; ==> ((1 a) (2 b) (3 c))
Though this will make lists and not pairs. However a unzip is almost the same except that you will take a list of lists instead of variable number of arguments:
(define (unzip1 zipped-list)
(apply map list zipped-list))
; or reuse zip
(define (unzip1 zipped-list)
(apply zip zipped-list))
(unzip1 '((1 a) (2 b) (3 c))) ; ==> ((1 2 3) (a b c))
First off, if anyone can find a question where this has already been answered, let me know. All I can find are functions that remove duplicates.
Anyhow, I am trying to write a scheme function (delete V L) that takes a value and a list as arguments, and removes that value from the list and all its sublists. For example, given the following input:
> (deep-delete 3 '(1 2 3 (4 3) 5 (6 (3 7)) 8))
It would yield:
(1 2 (4) 5 (6 (7)) 8)
So far, this is what I have written, but I know that the if statement (which is to check to see if the element is a sub-list, which implies it too must be operated on) must be placed incorrectly. Also, I cannot wrap my brain around where I should be using cons and where I shouldn't, because I'm still confused about tracking the return values of the recursion. Can someone please take a look and explain what I'm doing wrong?
(define (delete V L)
(if (list? (car L)) (cons (delete V (car L) (cdr L)))
(cond
((null? L) L)
((equal? V (car L)) (delete V (cdr L)))
(else (cons (car L) (delete V (cdr L))))))))
I have a few comments on your code:
First, in your if statement you use (car L) without checking if L is empty.
Also, in line 2 of your code, you do: (delete V (car L) (cdr L)),
but cons takes two arguments, not three. And you forgot to recursively call delete on the cdr.
You wanted:
(cons (delete V (car L)) (delete V (cdr L)))
Why not use a single cond? Since there are several cases, using cond will make the recursive structure of your algorithm more apparent, and errors easier to catch.
See below.
(define (del V L)
(cond ((null? L) L)
((list? (car L))
(cons (del V (car L)) (del V (cdr L))))
((equal? V (car L)) (del V (cdr L)))
(else (cons (car L) (del V (cdr L))))))
This will recursively delete V from L.
(del 3 '(1 2 3 (4 3) 5 (6 (3 7)) 8))
==> (1 2 (4) 5 (6 (7)) 8)
This is quite easy to achieve with folding; here's an example in Racket using foldr:
(define (deep-delete elt lst (test equal?))
(foldr (lambda (e r)
(if (list? e)
(cons (deep-delete elt e test) r)
(if (test elt e) r (cons e r))))
null
lst))
testing
> (deep-delete 3 '(1 2 3 (4 3) 5 (6 (3 7)) 8))
'(1 2 (4) 5 (6 (7)) 8)
This removes subtrees from a tree (including atomic ones):
(define (remove-element needle haystack)
(let rec ((haystack haystack))
(cond
((equal? needle haystack) '())
((not (pair? haystack)) haystack)
((equal? needle (car haystack)) (rec (cdr haystack)))
((equal? needle (cdr haystack)) (cons (rec (car haystack)) '()))
(else (cons (rec (car haystack))
(rec (cdr haystack)))))))
(remove-element 'atom 'atom) ; => ()
(remove-element '(1 2 3) '((1 2 3) 1 2 3)) ; => ()
(remove-element '(1 2 3) '((1 2 3) 4 5 6)) ; => (4 5 6)
(remove-element '(1 2 3) '(3 2 1 2 3)) ; ==> (3 2)
(remove-element '3 '((1 2 3) 1 2 3)) ; ==> ((1 2) 1 2)
(remove-element '(1 2 3) '(1 2 3 4)) ; ==> (1 2 3 4)
the code "tsFunc" gets two lists as input and it will pairs each elements from two lists.
It works most of cases.
but then I find a bit strange behavior when I give 2 equal length of lists (e.g. '(1 2) '(3 4).... or '(a b c) '(1 2 3).... , it works strangely. first, here are code.
[problem 1]
(define (tsFunc lst1 lst2)
(define (helper ls1 ls2 rst)
(reverse (if (or (null? ls1) (null? ls2))
rst
(helper (cdr ls1) (cdr ls2)
(cons (cons (car ls1) (car ls2)) rst)))))
(helper lst1 lst2 '()))
the behavior like this:
1) correct behavior with uneven length of lists :
(tsFunc '(1 2 3) '(a b)) ====> output: ((1 . a) (2 . b))
2) strange behavior with even length of lists
: (tsFunc '(1 2 3) '(a b c)) ===> output (wrong): ((3 . c) (2 . b) (1 . a))
===> expected : ((1 . a) (2 . b) (3 . c))
when the two input lists are same length, what is happening?
do the tsFunc logic have different behavior between the input lists with same lengths vs. the input lists with different lengths?
(Note. as I know, the code needs to have "reverse" for the final result. so it is not because of "reverse" in the code)
[problem 2] with the result of tsFunc => tsFunc result: (1 . 2) (3 . 4) => try to implement product like this (1*2)+(3*4) = 14, so I have like this..
(define (func l1 l2)
(tsFunc (l1 l2) ;; line 2 - how to call tsFunc's result??
(foldl (lambda (acc pair) ;; line 3
(+ acc (* (car pair) (cdr pair)))) ;; line 4
'()
l1 l2))) ;; like this?? or ??
line 3 , 4 ok..that's the logic what to do, then, how to call tsFunc result to use it as input and.. two lists for the last line.. unclear..
The first problem is that you keep reversing the lists at each iteration, if you really need to reverse the output, do it just once at the end:
(define (tsFunc lst1 lst2)
(define (helper ls1 ls2 rst)
(if (or (null? ls1) (null? ls2))
(reverse rst)
(helper (cdr ls1) (cdr ls2)
(cons (cons (car ls1) (car ls2)) rst))))
(helper lst1 lst2 '()))
Now, for the second problem - the code doesn't even compile: you're not correctly calling the tsFunc procedure, and you're calling it in the wrong point. Also the initial value for the accumulator parameter is wrong - you can't use a list if you intend to return a number:
(define (func l1 l2)
(foldl (lambda (acc pair)
(+ acc (* (car pair) (cdr pair))))
0
(tsFunc l1 l2)))
Using the sample input in the question, here's how it would work:
(func '(1 3) '(2 4))
=> 14
In the above tsFunc takes '(1 3) and '(2 4) as inputs, transforming them into '((1 . 2) (3 . 4)) and then foldl preforms the operation (1*2)+(3*4) = 14, as expected.
Since you are allowed to use higher order functions, why not use just SRFI-1 List library fold?
#!r6rs
(import (rnrs base)
(only (srfi :1) fold)) ;; srfi-1 fold stop at the shortest list
(define (func lst1 lst2)
(fold (lambda (x y acc)
(+ acc (* x y)))
0
lst1
lst2))
(func '(1 3) '(2 4 8)) ; ==> 14
I'm using R5RS standart of Scheme implementation.
Now imagine you have to find out if an element '(2 3 4) is in a list '(1 2 3 4).
As for the example, and more strictly, you wish:
1. (is-in? '(2 3 4) '(1 2 3 4)) -> #f
2. (is-in? '(2 3 4) '(1 (2 3 4)) -> #t
Question: how to get that kind of behaviour, as in example 1?
Let me explain: when you search throught a list, you could use either car or cdr to get its parts. Now if you recursively go throught the list, you eventually get:
3. (cdr '(1 2 3 4)) -> '(2 3 4)
4. (cdr '(1 (2 3 4)) -> '((2 3 4))
So eventually, we got 2 lists. And you can see here, that sublist '(2 3 4) is contained by both results from 3 and 4.
Please see the contradiction of 3 and 4 with 1 and 2: while '(2 3 4) is not contained in '(1 2 3 4), recursive call of cdr returns '(2 3 4), which is equal to '(2 3 4) - and using equal? function, somewhere inside recursive calls, we eventually get #t for both 1 and 2:
5. (is-in? '(2 3 4) '(1 2 3 4)) -> #t
6. (is-in? '(2 3 4) '(1 (2 3 4)) -> #t
So how to get that kind of behaviour from 1? I want to have a function, which works with all different types of data. Here's my function, which works like 5 and 6 (throught should work as 1 and 2):
(define or (lambda (x y)
(cond ((eq? x y) (eq? x #t))
(#t #t)
)
)
)
(define and (lambda (x y)
(cond ((eq? x y) (eq? x #t))
(#t #f)
)
)
)
(define atom? (lambda (x)
(not (pair? x))
)
)
(define length (lambda (x)
(cond ((eq? x '()) 0)
((atom? x) 1)
(#t (+ (length (car x)) (length (cdr x))))
)
)
)
(define equal? (lambda (x y)
(cond ((and (atom? x) (atom? y)) (eq? x y))
((not (eq? (length x) (length y))) #f)
((not (and (pair? x) (pair? y))) #f)
(#t (and (equal? (car x) (car y)) (equal? (cdr x) (cdr y))))
)
)
)
(define is-in? (lambda (x y)
(cond ((equal? x y) #t)
(#t (cond ((pair? y) (or (is-in? x (car y)) (cond ((eq? (length y) 1) #f)
(#t (is-in? x (cdr y)))
)))
(#t #f)
)
)
)
)
)
Update:
What I want is to have a general function, which can tell you if some object is inside another object. I name entities object to emphasize that the function should work with any input values, simple or complicated like hell.
Example usages:
1. (is-in? 1 '(1 2 3)) ;-> #t
2. (is-in? '(1) '(1 2 3)) ;-> #f
3. (is-in? '(2 . 3) '(1 2 . 3)) ;-> #f
4. (is-in? '(2 . 3) '(1 (2 . 3))) ;-> #t
5. (is-in? '2 '(1 2 . 3)) ;-> #t
6. (is-in? '(2) '(1 2 . 3)) ;-> #f
7. (is-in? '(1 2 (3 4 (5 6 . (7 . 8)) 9) 10 11 (12 . 13)) '(1 (2 3 ((4 ((6 (3 . ((1 2 (3 4 (5 6 . (7 . 8)) 9) 10 11 (12 . 13)))) 3) 4)) 5) 2))) ;-> #t
8. (is-in? '(2 3 4) '((1 (2 3 4)) (1 2 3 4))) ;-> #t
9. (is-in? '(2 3 4) '(1 2 3 4)) ;-> #f
10. (is-in? '(2 3 4) '(1 (2 3 4))) ;-> #t
11. (is-in? '(1) '(1)) ;-> #t
First of all - why are you redefining and, or, equal? and length? those are built-in primitives. Also your definition of atom? is wrong, it should be:
(define (atom? x)
(and (not (pair? x))
(not (null? x))))
I guess you need to implement this from scratch as part of a homework. Let's see how that can be accomplished, fill-in the blanks to get your answer:
(define (is-in? ele lst)
(or <???> ; trivial case: ele == list
(member? ele lst))) ; call helper procedure
(define (member? ele lst)
(cond ((null? lst) ; if the list is empty
<???>) ; then the element is not in the list
((atom? lst) ; if the list is not well-formed
(equal? <???> <???>)) ; then test if ele == list
(else ; otherwise
(or (equal? ele <???>) ; test if ele == the 1st element in the list
(member? ele <???>) ; advance the recursion over the `car`
(member? ele <???>))))) ; advance the recursion over the `cdr`
Notice that the second case in member? is needed because in the examples given there are malformed lists (ending in a non-null value). The above solution will correctly handle all of the examples provided in the question.
(define (is-in? e lst)
(cond ((null? lst) #f) ; if list is empty, we failed
((eq? e (car lst)) #t) ; check 1st element of list
((list? (car lst)) (is-in? e (append (car lst) (cdr lst)))) ; search inside car if it is a list
(#t (is-in? e (cdr lst))))) ; check rest of list
You can replace eq? with something more elaborate to handle other definitions of equality.
Note: this assumes that all sequences are lists; you'll have to tweak it to handle dotted-pairs that are not lists.