(define (associate lst)
(if (or (null? lst) (= (length lst) 1))
'()
(cons (cons (car lst) (cadr lst)) (associate (cddr lst)))))
(define (disassociate lst)
;(display (caar lst))
(if (null? lst)
'()
(cons (cons (caar lst) (cdar lst)) (disassociate (cdr lst)))))
(display (disassociate '((a . 1) (b . 2) (c . 3))))
(newline)
(display (associate '(a 1 b 2 c)))
(newline)
OUTPUT:
;; with list
((a 1) ((b 2) ((c 3) ())))
((a . 1) (b . 2))
;; with cons
((a . 1) (b . 2) (c . 3))
((a . 1) (b . 2))
I'm trying to flatten an association list in Scheme but the brackets
keep turning up even when I change the list to cons. Am I doing something wrong?
There's an error in the way you're creating the list in disassociate. Try this:
(define (disassociate lst)
(if (null? lst)
'()
(cons (caar lst)
(cons (cdar lst)
(disassociate (cdr lst))))))
Or alternatively, using list*:
(define (disassociate lst)
(if (null? lst)
'()
(list* (caar lst)
(cdar lst)
(disassociate (cdr lst)))))
The above assumes that the association list is using cons to stick together the values, notice how the output list is created by consing the first element, then the second and then calling the recursion. On the other hand, if the association list was created using list to stick together the values, then this is the way to disassociate it:
(define (disassociate lst)
(if (null? lst)
'()
(cons (caar lst)
(cons (cadar lst) ; here's the change
(disassociate (cdr lst))))))
Or alternatively:
(define (disassociate lst)
(if (null? lst)
'()
(list* (caar lst)
(cadar lst) ; here's the change
(disassociate (cdr lst)))))
A more idiomatic solution would be to use higher-order procedures to process the input list. Here's how, using foldr for the two association list variants described in the question:
; associations created with cons
(define (disassociate lst)
(foldr (lambda (pr ac) (list* (car pr) (cdr pr) ac))
'()
lst))
; associations created with list
(define (disassociate lst)
(foldr (lambda (pr ac) (list* (car pr) (cadr pr) ac))
'()
lst))
Your disassociate line needs to read:
(cons (caar lst) (cons (cdar lst) (disassociate (cdr lst)))))
quasi-quotation! Sometimes these are more clearly implemented using quasi-quotation because quasi-quotation lets you see the resulting list structure.
(define (associate lst)
(if (or (null? lst) (null? (cdr lst)))
'()
`(,(cons (car lst) (cadr lst)) ,#(associate (cddr lst)))))
(define (disassociate lst)
(if (null? lst)
'()
`(,(caar lst) ,(cdar lst) ,#(disassociate (cdr lst)))))
> (associate (disassociate '((a . 1) (b . 2))))
((a . 1) (b . 2))
Note that you could also write the associate quasi-quotation as:
`((,(car lst) . ,(cadr lst)) ,#(associate (cddr list)))))
but that starts getting harder to read for me even though it makes the association pair explicit.
Related
The code I've written:
(define (make-list lst)
(cond [(null? lst) '()]
[(member (car lst) (cdr lst)) (make-list (cdr L))]
[else (cons (car lst) (duplicates (cdr lst)))]))
I want (make-list '(a (a b b (c b) 3) 5 5.0 (e s) (s e s))))) to return:
(a (a b (c b) 3) 5 (e s))
But my procedure returns
'((a b b (c b) 3) 5 5.0 (e s) (s e s))
So it doesn't really do anything besides removing the first element, it doesn't go into the nested lists. Any help would be appreciated
I'm assuming duplicates and L are forgotten rename mistakes. make-list isn't really a good name since it is a well known procedure in the list library of R7RS, originally from SRFI-1 List library. remove-duplicates might be a better fit?
(define (remove-duplicates lst)
(cond [(null? lst) '()]
[(member (car lst) (cdr lst))
(remove-duplicates (cdr lst))]
[else
(cons (car lst) (remove-duplicates (cdr lst)))]))
Now this does all the elements in the given list and it only concerns itself with the top level list. sub lists are compared too:
(remove-duplicates '(a (b c) (b c) a))
; ==> ((b c) a)
You need to instead of just making a list with the first element also check if the first element is a list and do remove-duplicates on both parts. Thus you need to do add a term like this:
(define (remove-duplicates lst)
(cond [(null? lst) '()]
[(member (car lst) (cdr lst))
(remove-duplicates (cdr lst))]
[(list? (car lst)) ; or pair?
(cons (remove-duplicates (car lst))
(remove-duplicates (cdr lst)))]
[else
(cons (car lst)
(remove-duplicates (cdr lst)))]))
(remove-duplicates '((a b b a) (a b b a)))
; ==> ((b a))
I'm attempting to create a function for flattening lists in the R5RS language in scheme and am experiencing the issue where my function simply returns the input list without removing the parenthesis. I figured this was due to the extra cons, but when I remove it the output becomes the list without the elements that were in the parenthesis. Can someone point me in the right direction?
(define (denestify lst)
(cond ((null? lst)'())
((list? (car lst))(cons (denestify (cons (car (car lst))(cdr (car lst))))
(denestify (cdr lst))))
(else (cons (car lst)(denestify (cdr lst))))))
This shows how to convert Óscar López answer into one that doesn't use append and is also tail recursive:
(define (denestify-helper lst acc stk)
(cond ((null? lst)
(if (null? stk) (reverse acc)
(denestify-helper (car stk) acc (cdr stk))))
((pair? (car lst))
(denestify-helper (car lst) acc (cons (cdr lst) stk)))
(else
(denestify-helper (cdr lst) (cons (car lst) acc) stk))))
(define (denestify lst) (denestify-helper lst '() '()))
(denestify '(1 (2 (3 4 (5) (6 (7) (8)) (9))) 10))
Note how it uses the accumulator to build up the list in reverse and also a list as a stack.
Which results in
'(1 2 3 4 5 6 7 8 9 10)
as expected.
After I posted this I thought of this change:
(define (denestify-helper lst acc stk)
(cond ((null? lst)
(if (null? stk) (reverse acc)
(denestify-helper (car stk) acc (cdr stk))))
((pair? (car lst))
(denestify-helper (car lst) acc (if (null? (cdr lst))
stk
(cons (cdr lst) stk))))
(else
(denestify-helper (cdr lst) (cons (car lst) acc) stk))))
Which eliminates some useless consing by effectively doing tail-call optimization on our stack. One could go further and optimize handling of one element lists.
If you want to flatten a list of lists, then you have to use append to combine each sublist. Besides, your implementation is overly complicated, try this instead:
(define (denestify lst)
(cond ((null? lst) '())
((pair? (car lst))
(append (denestify (car lst))
(denestify (cdr lst))))
(else (cons (car lst) (denestify (cdr lst))))))
For example:
(denestify '(1 (2 (3 4 (5) (6 (7) (8)) (9))) 10))
=> '(1 2 3 4 5 6 7 8 9 10)
I have a list '(1 2 1 1 4 5) and want output list as '((1 3)(2 1)(4 1)(5 1)). I have written a small code but I am stuck with how to calculate the cardinality for each number and then put it as pair in list. Can anyone please look at my code and give some ideas?
(define set2bags
(lambda (randlist)
(cond ((null? randlist) '())
(else
(sort randlist)
(makepairs randlist)))))
(define makepairs
(lambda (inlist)
(let ((x 0)) ((newlist '()))
(cond ((zero? (car inlist)) '())
(else
(eq? (car inlist)(car (cdr inlist)))
(+ x 1)
(makepairs (cdr inlist))
(append newlist (cons (car inlist) x)))))))
Your current solution is incorrect - it doesn't even compile. Let's start again from scratch, using a named let for traversing the input list:
(define set2bags
(lambda (randlist)
(cond ((null? randlist) '())
(else (makepairs (sort randlist >))))))
(define makepairs
(lambda (inlist)
(let loop ((lst inlist)
(prv (car inlist))
(num 0)
(acc '()))
(cond ((null? lst)
(cons (list prv num) acc))
((= (car lst) prv)
(loop (cdr lst) prv (add1 num) acc))
(else
(loop (cdr lst) (car lst) 1 (cons (list prv num) acc)))))))
Now it works as expected:
(set2bags '(1 2 1 1 4 5))
=> '((1 3) (2 1) (4 1) (5 1))
The trick is keeping a counter for the cardinality (I called it num), and incrementing it as long as the same previous element (I named it prv) equals the current element. Whenever we find a different element, we add a new pair to the output list (called acc) and reset the previous element and the counter.
Your code is fairly hard to read without proper formating.
I notice a two branch cond, which is easier to read as an if.
In your else clause of set2bags, you call (sort randlist) but leave it as is. You actually want to use this in the next s-expression (makepairs (sort randlist))
So far a pretty good idea.
Now in makepairs you should have better abstraction, say let variables like-first and unlike-first. If the inlist is null, then the function should be the null list, else it's the pair with the car being the list of the car of like-first and the length of like-first and the cdr being the result of calling makepairs on the unlike-first list
(define (makepairs inlist)
(let ((like-first (filter (lambda (x) (equal? x (car inlist)) inlist))
(unlike-first (filter (lambda (x) (not (equal? x (car inlist))) inlist)))
(if (null? inlist)
'()
(cons (list (car inlist) (length like-first)) (makepairs unlike-first)))))
more effecient version
(define (makepairs inlist)
(if (null? inlist)
'()
(let loop ((firsts (list (car inlist)))
(but-firsts (cdr inlist)))
(if (or (null? but-firsts)
(not (equal? (car firsts) (car but-firsts))))
(cons (list (car firsts) (length firsts))
(makepairs but-firsts))
(loop (cons (car but-firsts) firsts) (cdr but-firsts))))))
]=> (makepairs (list 1 1 1 2 4 5))
;Value 17: ((1 3) (2 1) (4 1) (5 1))
If you have your own implementation of sort, say a mergesort you could write this right into the merge part for the best effeciency.
(define (set2bags lst)
(mergesort2bags lst <))
(define (mergesort2bags lst pred)
(let* ((halves (divide-evenly lst))
(first-half (car halves))
(other-half (cadr halves)))
(cond ((null? lst) '())
((null? (cdr lst)) (list (list (car lst) 1)))
(else
(merge-bags
(mergesort2bags first-half pred)
(mergesort2bags other-half pred)
pred)))))
(define (divide-evenly lst)
(let loop
((to-go lst)
(L1 '())
(l2 '()))
(if (null? to-go)
(list L1 L2)
(loop (cdr to-go) (cons (car to-go) L2) L1))))
(define (merge-bags L1 L2 pred)
(cond ((null? L1) L2)
((null? L2) L1)
((pred (caar L1) (caar L2))
(cons (car L1) (merge-bags (cdr L1) L2 pred)))
((equal? (caar L1) (caar L2))
(cons (list (caar L1) (+ (cadar L1) (cadar L2)))
(merge-bags (cdr L1) (cdr L2) pred)))
(else (cons (car L2) (merge-bags L1 (cdr L2) pred)))))
(mergesort2bags (list 1 2 1 1 4 5) <)
;Value 46: ((1 3) (2 1) (4 1) (5 1))
I'm thinking for very large datasets with a lot of repetition this method would pay off.
I'd like to create a Scheme function that yields true if it is passed a list that is composed entirely of identical elements. Such a list would be '(1 1 1 1). It would yield false with something like '(1 2 1 1).
This is what I have so far:
(define (list-equal? lst)
(define tmp (car lst))
(for-each (lambda (x)
(equal? x tmp))
lst)
)
Clearly this is incorrect, and I'm new to this. I guess I'm unable to express the step where I'm supposed to return #t or #f.
Thanks in advance!
EDIT:
I fiddled a bit and found a solution that seems to work very well, and with a minimal amount of code:
(define (list-equal? lst)
(andmap (lambda (x)
(equal? x (car lst)))
lst))
Thanks again for the help everyone.
Minimal amount of code, if you don't care that it only works for numbers:
(define (list-equel? lst)
(apply = lst))
Examples:
> (list-equel? '(1 1 2 1))
#f
> (list-equel? '(1 1 1 1))
#t
> (list-equel? '(1))
#t
The andmap solution is nice, but if andmap is not available, you can use this. It uses basic operations (and, or, null check, equality check) and handles empty lists and one element lists. Similar to Sean's implementation, but no helper definition is necessary.
(define (list-equal? args)
(or (or (null? args)
(null? (cdr args)))
(and (eq? (car args) (cadr args))
(list-equal? (cdr args)))))
Try something like this:
(define (list-equal? lst)
(define (helper el lst)
(or (null? lst)
(and (eq? el (car lst))
(helper (car lst) (cdr lst)))))
(or (null? lst)
(helper (car lst) (cdr lst))))
This might not be the cleanest implementation, but I think it will correctly handle the cases of empty lists and one-element lists.
In R6RS there's the for-all function, which takes a predicate and a list, and returns #t if the predicate returns true for all elements in the list and #f otherwise, which is exactly what you need here.
So if you're using R6RS (or any other scheme dialect that has the for-all function), you can just replace for-each with for-all in your code and it will work.
(define (list-equal? lst)
(if (= (cdr lst) null)
true
(and (equal? (car lst) (cadr lst))
(list-equal? (cdr lst)))))
Something like this should work:
(define (list-equal? lst)
(cond ((< (length lst) 2) #t)
(#t (and (equal? (car lst) (cadr lst))
(list-equal? (cdr lst))))))
The other answers in this thread all seem too complicated (I read through them all), so here's my take on it:
(define (all-equal? lst)
(define item (car lst))
(let next ((lst (cdr lst)))
(cond ((null? lst) #t)
((equal? item (car lst)) (next (cdr lst)))
(else #f))))
(It does not work with an empty list, by design. It's easy to add a (if (null? lst) #t ...) if necessary.)
A short, concise solution:
#lang racket
(define (all-equal? lst)
(for/and
([i (in-permutations lst)])
(equal? (first i) (second i))))
; TEST CASES
(require rackunit)
(check-false (all-equal? '(1 2 3)))
(check-true (all-equal? '(1 1 1)))
(check-true (all-equal? '()))
Note that this uses racket, so this may not work with your scheme implementation.
Yet another solution:
(define (all-same ls)
(cond
((or (null? ls)
(null? (cdr ls))) #t)
(else (and (equal? (car ls) (next ls))
(all-same (cdr ls)))))))
(define (next ls)
(cond
((or (null? ls)
(null? (cdr ls))) '())
(else (cadr ls)))))
For is bad in these languages. Try
(define list-equal?
(lambda (lst)
(if (= lst null)
(true)
(foldr = (car lst) (cdr lst))
)))
My implementation of flatten looks like this:
(define flatten
(lambda (lst)
(if (null? lst)
lst
(append
(rtn-lst (car lst))
(flatten (cdr lst))))))
(define rtn-lst
(lambda (lst)
(cond
((null? lst)
empty)
((atom? lst)
(list lst))
(else
(flatten lst)))))
While standard implementation is:
(define (flatten lst)
(cond
((null? list)
empty)
((list? (car lst))
(append (flatten (car lst)) (flatten (cdr lst))))
(else
(cons (car lst) (flatten (cdr lst))))))
Apart from the obvious verboseness, what else is wrong with my code?
I'd try this:
(define rtn-lst
(lambda (lst)
(cond
((list? lst)
(if (null? lst)
empty
(flatten-list lst)))
((atom? lst)
(list lst))
(else
(flatten-list lst)))))
Probably we have different implementations of Scheme.
EDIT:
With modified else branch:
(define rtn-lst
(lambda (lst)
(cond
((list? lst)
(if (null? lst)
empty
(flatten-list lst)))
(else
(list lst)))))
I would consider atom? to be wrong. You want to know if the lst is a list, so use list?. atom? can return false on vector or string for some implementations. But i do not know for sure. The rest is quit good.
How about something like this:
(define foo
(lambda (e)
(cond ((pair? e) `(,#(foo (car e)) ,#(foo (cdr e))))
((null? e) '())
(else (list e)))))
Where for example:
> (foo '(((2 3) (4 . 5) 8)))
(2 3 4 5 8)
Does this do what you want?
How about something like this:
(define (flatten x y)
(if (null? x)
y
(if (list? (car x))
(flatten (append (car x) (cdr x)) y)
(flatten (cdr x) (append y (list (car x)))))))
(define (flat x)
(flatten x '()))
> (flat '(1(2(3(4(5)6)7)8)9))
(1 2 3 4 5 6 7 8 9)
and closure version:
(define (flatten x)
(define (flatten x y)
(if (null? x)
y
(if (list? (car x))
(flatten (append (car x) (cdr x)) y)
(flatten (cdr x) (append y (list (car x)))))))
(flatten x '()))
> (flatten '(1(2(3(4(5)6)7)8)9))
(1 2 3 4 5 6 7 8 9)