In class we wrote an interpreter for a made up language (lanG) using the following racket structs.
(struct const (n))
(struct bool (b))
(struct join (e1 e2))
(struct if-then-else (b e1 e2))
(struct negate (e))
(struct add (e1 e2))
(struct multiply (e1 e2))
(struct head (e)) ;returns the head of the list
(struct tail (e)) ;returns the tail of the list
(struct biggerThan (e1 e2))
Macros for this language are defined as racket functions. A simple example would be:
(define (threeTimes x)
(add x (add x x)))
And using it would look like:
(lanG (threeTimes (const 3)))
which would produce an answer:
(const 9)
Now to my problem. There was a task on the exam where we had to write a macro sumAtEvenAndOdd, which would sum a list of lanG constants,
made with the join struct and return a pair of values consisting of the sum of elements at even positions and the sum of elements
at the odd positions.
An example of such a list would be:
(join (const 3) (join (const 2) (const 5))) ;lanG list with no null at the end
And its result would be:
(join (const 2) (const 8))
I tried solving this by converting the list into a racket list, ziping the positions with the elements, filtering the odd or even elements out of the list,
and producing the pair using the sums of those lists. This works but I am overcomplicating. Professor said the solution is about 5 lines long.
I thank you in advance for all your help.
I assume there are also predicates to identify a const and a join - let's call them const? and join?.
Supposing we had a function for adding up every other item of a list, sumAtEvenAndOdd could look like this:
(define (sumAtEvenAndOdd xs)
(join (sumEveryOther (tail xs)) (sumEveryOther xs)))
and then sumEveryOther could be implemented like this:
(define (sumEveryOther x)
(if-then-else (const? x)
x
(if-then-else (join? (tail x))
(add (head x) (sumEveryOther (tail (tail x))))
(head x))))
This is of course not optimal, since it traverses the list twice, but it's short ("exam-size") and implemented entirely within lanG.
A slightly longer solution that only traverses the list once, using accumulators:
(define (sumEvenOdd x evens odds odd?)
(if-then-else (const? x)
(if-then-else odd?
(join evens (add odds x))
(join (add evens x) odds))
(if-then-else odd?
(sumEvenOdd (tail x) evens (add (head x) odds) (negate odd?))
(sumEvenOdd (tail x) (add (head x) evens) odds (negate odd?)))))
(define (sumAtEvenAndOdd xs)
(sumEvenOdd xs 0 0 (bool #t)))
So join is like a pair where join-e2 could be a join?. To loop through it you do the same as with pair? with a dotted list since a proper list in you example ended with a const.
(let loop ((lst '(1 2 3 4 5 6 . 7)) (o 0) (e 0) (odd? #t))
(let* ((ele (if (pair? lst) (car lst) lst))
(no (if odd? (+ ele o) o))
(ne (if odd? e (+ ele e))))
(if (pair? lst)
(loop (cdr lst) no ne (not odd?))
(cons no ne))))
Here is a simple recursive solution.
(define (sum-even/odd xs)
(if (null? xs)
(values 0 0)
(call-with-values
(λ () (sum-even/odd (cdr xs)))
(λ (e o) (values (+ (car xs) o) e)))))
> (sum-even/odd '(1 2 3 4 5 6 7))
16
12
Related
I have a partition for a quicksort:
(define (partition pivot lst)
((lambda (s) (s s lst list))
(lambda (s l* c)
(if (null? l*)
(c '() '())
(let ((x (car l*)))
(s s (cdr l*)
(lambda (a b)
(if (< x pivot)
(c (cons x a) b)
(c a (cons x b))))))))))
partition code source
Testing:
=>(partition '5 '(1 3 5 7 9 8 6 4 2))
;Value: ((1 3 4 2) (5 7 9 8 6))
How can I implement this partition in a quicksort? I've tried this so far:
(define (quicksort lst)
(if (null? lst) '()
(let* ((y (car lst))
(pn (partition y (cdr lst))))
(append (quicksort (car pn))
(list y)
(quicksort (cdr pn))))))
First, your code is trivially fixed by changing one cdr to cadr:
(define (partition pivot lst)
((lambda (s) (s s lst list))
......)) ; ^^^^ `list` the top continuation
(define (quicksort lst)
(if (null? lst) '()
(let* ((y (car lst))
(pn (partition y (cdr lst))))
(append
(quicksort (car pn))
(list y)
(quicksort (cadr pn))))))
;; ^^^^ cdr --> cadr
because the top continuation used in partition is list, and so the call
(partition pivot lst)
is equivalent to the call
(list { x IN lst SUCH THAT x < pivot }
{ x IN lst SUCH THAT x >= pivot } )
(the parts in {...} are pseudocode, where we don't care about the implementation, just the results)
And so to access the two parts of that list built by partition you need to use car and cadr.
Or you could keep the cdr in the accessing part of your code in quicksort if you'd change that top continuation to cons:
(define (partition pivot lst)
((lambda (s) (s s lst cons))
......)) ; ^^^^ `cons` the top continuation
(define (quicksort lst)
(if (null? lst)
'()
(let* ((y (car lst))
(pn (partition y (cdr lst))))
(append
(quicksort (car pn))
(list y)
(quicksort (cdr pn))))))
;; ^^^^ `cdr` works fine with `cons`
This because of the general principle in programming, where the functions used to build our data dictate which functions are to be used to access that data:
(list <A> <B> )
car cadr
(cons <A> <B> )
car cdr
( this particular correspondence is because (list <A> <B>) is the same as (cons <A> (cons <B> '())) and (cadr <C>) is the same as (car (cdr <C>)): )
(list <A> <B> )
=
(cons <A> (cons <B> '()))
car cdr
car
And conversely, the functions we use to access our data dictate the implementation of the function which must be used to build that data.
Of course that way of coding in your question is considered unnecessarily convoluted by modern standards since it emulates recursion through argument passing and reuse, -- just like in the famous Y combinator, -- but any Scheme worthy of its name already supports recursion.
So this partition would normally be written as the fully equivalent yet more readable code using the "named let" construct,
(define (partition pivot lst)
(let s ( (l* lst) ; first `l*` is `lst`
(c cons) ) ; top `c` is `cons`
(if (null? l*)
(c '() '())
(let ((x (car l*)))
(s (cdr l*)
(lambda (a b)
(if (< x pivot)
(c (cons x a) b)
(c a (cons x b)))))))))
except the name loop is conventionally used in place of s here (which itself most probably is intended as the shortening of "self").
But the true trouble with your quicksort/partition pair is algorithmic.
Yes I say pair (in non-cons sense of course) since the two go together -- just as with the data access/creation functions which must work together too.
Implementation of one dictates the implementation of the other -- in both directions, too. partition's code dictates quicksort's, or if we'd written quicksort first, we'd need to implement the partition in the corresponding way -- so that the two work together. Which means quicksort indeed producing the correct results, turning any input list into a sorted one:
(quicksort lst) --->
{ xs SUCH THAT
FOR ANY splitting xs = { ..., x, ...ys }
AND ANY splitting ys = { ..., y, ... }
IT HOLDS THAT x <= y
AND ALSO xs is a permutation of lst
(which implies (length lst) == (length xs))
}
So then, what is that trouble? It is that the true quicksort does no work whatsoever after the partitioning. None:
(define (quicksort! lst)
(if (null? lst)
'()
(let* ((y (car lst))
(pn (partition! y lst)))
(quicksort! (car pn)) ; no `append`, NB!
(quicksort! (cdr pn))))) ; no (list y) either
How is that even possible? What kind of partition! implementation would make that work? Well, most certainly not a functional one.
Instead it must be changing (i.e. mutating) the very lst itself somehow:
{ a, b, c, ....., k, l, m, ..... }
-->
{ d, e, ...., p, n, o, ..... }
~~~~~~~~~~~ ~~~~~~~~~~~
where we denote with p the partition point -- so that indeed all that's left to do after this kind of partitioning "in-place" is to sort the first part, and then to sort the second part, -- and then there's nothing more left to be done, after that! Which was the key insight in the original Tony Hoare's formulation of it:
TO SORT
{ a, b, c, ....., k, l, m, ..... } DO:
PARTITION it into
{ d, e, ...., p, n, o, ..... } AND THEN:
~~~~~~~~~~~ ~~~~~~~~~~~
SORT! SORT!
DONE.
This partitioning is usually implemented with swap! which actually swaps two elements in the underlying data structure. Most usually that data structure is an array with its facilities to change the value stored in it at any given index.
But it can also be a list, where the change i.e. mutation can be done with the set-car! primitive.
Looks like we'd need to build a list of cdrs out of the input list, and another one in reverse, -- to be able to iterate over them in both directions, back and forth, -- to make that happen.
I'll leave that for another day, for now.
Once you have the partition, there is still a small step to do.
Take care, you need to be sure partition splits the input in smaller sets all the time. In other word, partition not to return some empty set. The pivot can go in any of the sets and use this fact to check that you do not return an empty set, in case your comparison operator does not really decrease the size of the input. This is why I inserted the equality operator -- to be able to check if I insert the pivot in the first returned set or in the second one.
(define (partition pivot lst ret)
((lambda (s)
(s s lst
(lambda (a b p*)
(if (and (null? a) (null? b))
(ret (list pivot) (cdr p*))
(if (null? a)
(ret p* b)
(if (null? b)
(ret a p*)
(if (< (car b) pivot)
(ret a (append p* b))
(if (< (car a) pivot)
(ret (append a p*) b)
(error "never here")))))))))
(lambda (s l* c)
(if (null? l*)
(c '() '() '())
(let ((x (car l*)))
(s s (cdr l*)
(lambda (a b p*)
(if (= x pivot)
(c a b (cons pivot p*))
(if (< x pivot)
(c (cons x a) b p*)
(c a (cons x b) p*))))))))))
(define choose-pivot car)
In a real implementation, you will all the time use vectors and this is why the append will not be present, as, sorting on the place, at the end of partition, both sides will be sorted relatively one to the other. Here, we need to reassemble the 2 sides using append:
(define (quicksort lst)
(if (null? lst) '()
(if (null? (cdr lst))
lst
(let* ((pivot (choose-pivot lst)))
(partition pivot lst
(lambda (p< p>)
(append
(quicksort p<)
(quicksort p>))))))))
A test:
1 ]=> (quicksort '(1 3 5 7 9 8 6 4 2))
;Value: (1 2 3 4 5 6 7 8 9)
1 ]=> (quicksort '(1 9 3 8 5 7 7 6 9 5 8 4 6 3 4 2 2 1))
;Value: (1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9)
I used as pivot the first element of the input to split, but you can redefine the choose-pivot to select other element.
In practice, this algorithm is used in combination with other sorts -- when the input has fewer than 4-8 elements, the quicksort is not recurred any more, but other sorting is used for the lowest cases of recurrence relation.
I used directly < in the code -- you can insert it as a parameter in case you prefer a more generic procedure... In any case, the operator that you use needs to simulate the equality and different of in the same time.
UPDATE I have updated the partition, such that to consider duplicated elements. In my first version, it ignored duplicated elements.
I'm working on this procedure which is supposed to return a list of pairs from 2 given lists. So for example (pairs '(1 2 3) '(a b c)) should return '((1.a) (2.b) (3.c)).
This is my logic so far. I would take the first element of each list and recursively call the procedure again with cdr as the new arguments. My result is returning a list such as this: (1 a 2 b 3 c)
Where is my logic going wrong? I know there is a list missing somewhere, but I'm not an expert at Scheme.
Any suggestions?
(define pairs
(lambda (x y)
(if (or (null? x) (null? y))
'()
(cons (car x)
(cons (car y)
(pairs (cdr x)(cdr y)))))))
(pairs '(1 2 3) '(a b c))
Notice that you produce a value that prints as (1 . 3) by evaluating (cons 1 3). However in your program you are doing (cons 1 (cons 3 ...)) which will prepend 1 and 3 to the following list.
In other words: Instead of (cons (car x) (cons (car y) (pairs ...))
use (cons (cons (car x) (car y) (pairs ...)).
Using map simplifies it a lot:
(define (pairs x y)
(map (λ (i j) (list i j)) x y))
Testing:
(pairs '(1 2 3) '(a b c))
Output:
'((1 a) (2 b) (3 c))
The result you're looking for should look like this:
((1 a) (2 b) (3 c))
In reality this structure is similar to this:
(cons
(cons 1 a)
(cons
(cons 2 b)
(cons
(cons 3 c)
'()
)
)
)
So what you're looking for is to append pairs to a list instead of adding all items to the list like you do. Simply your result looks like this:
(1 (2 (pairs ...)))
Your code should look like this:
(define pairs
(lambda (x y)
(if (or (null? x) (null? y))
'()
(cons
(cons (car x) (car y))
(pairs (cdr x) (cdr y))))))
This code might work, but it isn't perfect. We could make the code pass the list we create as a third parameter to make the function tail recursive.
You'd have something like this:
(define pairs
(lambda (x y)
(let next ((x x) (y y) (lst '()))
(if (or (null? x) (null? y))
(reverse lst)
(next (cdr x)
(cdr y)
(cons
(cons (car x) (car y))
lst))))))
As you can see, here since we're adding next element at the beginning of the list, we have to reverse the lst at the end. The difference here is that every time next is called, there is no need to keep each state of x and y in memory. When the named let will return, it won't be necessary to pop all the values back to where it called. It will simply return the reversed list.
That said, instead of using reverse we could simply return lst and use (append lst (cons (car x) (car y))) which would append the pair at the end of the list... Since lists are linked lists... in order to append something at the end of the list, scheme has to walk over all list items... which migth not be good with big list. So the solution is to add everything and at the end reorder the list as you wish. The reverse operation would happen only once.
Currently I have
(define filter
(λ (f xs)
(letrec [(filter-tail
(λ (f xs x)
(if (empty? xs)
x
(filter-tail f (rest xs)
(if (f (first xs))
(cons (first xs) x)
'()
)))))]
(filter-tail f xs '() ))))
It should be have as a filter function
However it outputs as
(filter positive? '(-1 2 3))
>> (3 2)
but correct return should be (2 3)
I was wondering if the code is correctly done using tail-recursion, if so then I should use a reverse to change the answer?
I was wondering if the code is correctly done using tail-recursion.
Yes, it is using a proper tail call. You have
(define (filter-tail f xs x) ...)
Which, internally is recursively applied to
(filter-tail f
(some-change-to xs)
(some-other-change-to x))
And, externally it's applied to
(filter-tail f xs '())
Both of these applications are in tail position
I should use a reverse to change the answer?
Yep, there's no way around it unless you're mutating the tail of the list (instead of prepending a head) as you build it. One of the comments you received alluded to this using set-cdr! (see also: Getting rid of set-car! and set-cdr!). There may be other techniques, but I'm unaware of them. I'd love to hear them.
This is tail recursive, requires the output to be reversed. This one uses a named let.
(define (filter f xs)
(let loop ([ys '()]
[xs xs])
(cond [(empty? xs) (reverse ys)]
[(f (car xs)) (loop (cons (car xs) ys) (cdr xs))]
[else (loop ys (cdr xs))])))
(filter positive? '(-1 2 3)) ;=> '(2 3)
Here's another one using a left fold. The output still has to be reversed.
(define (filter f xs)
(reverse (foldl (λ (x ys) (if (f x) (cons x ys) ys))
'()
xs)))
(filter positive? '(-1 2 3)) ;=> '(2 3)
With the "difference-lists" technique and curried functions, we can have
(define (fold c z xs)
(cond ((null? xs) z)
(else (fold c (c (car xs) z) (cdr xs)))))
(define (comp f g) (lambda (x) ; ((comp f g) x)
(f (g x))))
(define (cons1 x) (lambda (y) ; ((cons1 x) y)
(cons x y)))
(define (filter p xs)
((fold (lambda (x k)
(if (p x)
(comp k (cons1 x)) ; nesting's on the left
k))
(lambda (x) x) ; the initial continuation, IC
xs)
'()))
(display (filter (lambda (x) (not (zero? (remainder x 2)))) (list 1 2 3 4 5)))
This builds
comp
/ \
comp cons1 5
/ \
comp cons1 3
/ \
IC cons1 1
and applies '() to it, constructing the result list in the efficient right-to-left order, so there's no need to reverse it.
First, fold builds the difference-list representation of the result list in a tail recursive manner by composing the consing functions one-by-one; then the resulting function is applied to '() and is reduced, again, in tail-recursive manner, by virtues of the comp function-composition definition, because the composed functions are nested on the left, as fold is a left fold, processing the list left-to-right:
( (((IC+k1)+k3)+k5) '() ) ; writing `+` for `comp`
=> ( ((IC+k1)+k3) (k5 '()) ) ; and `kI` for the result of `(cons1 I)`
<= ( ((IC+k1)+k3) l5 ) ; l5 = (list 5)
=> ( (IC+k1) (k3 l5) )
<= ( (IC+k1) l3 ) ; l3 = (cons 3 l5)
=> ( IC (k1 l3) )
<= ( IC l1 ) ; l1 = (cons 1 l3)
<= l1
The size of the function built by fold is O(n), just like the interim list would have, with the reversal.
I defined a simple lazy list of all integers from zero:
(define integers-from
(lambda (n)
(cons n
(lambda () (integers-from (+ 1 n))))))
(define lz (integers-from 0))
I also coded an accumaltor that gets a lazy list as a parameter
(define lz-lst-accumulate
(lambda (op initial lz)
(if (null? lz)
initial
(cons (op (head lz) initial)
(lambda () (lz-lst-accumulate op (op initial (head lz)) (tail lz)))))))
Does this accumaltor answer the format of lazy lists?
Here is a simple test of the accumulator:
(define acc (lz-lst-accumulate * 1 lz))
(take acc 4)
=> '(1 2 6 24)
take is a helper function that creates a list from the first n elements of a lazy list:
(define head car)
(define tail
(lambda (lz-lst)
((cdr lz-lst)) ))
(define take
(lambda (lz-lst n)
(if (= n 0)
(list)
(cons (car lz-lst)
(take (tail lz-lst) (sub1 n)))) ))
In your lz-lst-accumulate you calculate once (op (head lz) initial) and then also (op initial (head lz)). This is inconsistent; both should be the same and actually calculated only once, since it's the same value:
(define lz-lst-accumulate
(lambda (op initial lz)
(if (lz-lst-empty? lz)
initial
(let ((val (op (head lz) initial)))
(cons val
(lambda () (lz-lst-accumulate op val (tail lz))))))))
It works in your example with numbers only because you use the type-symmetrical operation *. With cons it wouldn't work.
Other than that it's OK. lz-lst-accumulate is usually known as left fold (scanl in Haskell, actually, since you produce the progression of "accumulated" values, foldl f z xs = last (scanl f z xs)).
re: your version of take, it is forcing one too many elements of a stream. Better make it
(define take
(lambda (lz n)
(if (or (<= n 0) (lz-lst-empty? lz))
(list)
(if (= n 1)
(list (car lz)) ; already forced
(cons (car lz)
(take (tail lz) (sub1 n)))))))
so that it only forces as many elements as it has to produce, and not one more (which might be e.g. divergent, like (/ 1 0), invalidating the whole calculation for no reason).
That way, the counter-example in SRFI 41 (of (take 4 (stream-map 1/ (ints-from-by 4 -1)))) will just work (it calculates (1/4 1/3 1/2 1/1) without forcing 1/0, which the usual version of take, like the one you're using, would do).
I can't seem to figure out how to write this function. What I am trying to write is a function expand that takes a list lst as a parameter of the form '(a (2 b) (3 c)) and is evaluated to '(a b b c c c)
This looks like homework, so I'm not giving you a straight answer. Instead, I'll give you some pointers in the right direction. The most useful hint, is that you should split the problem in two procedures, one for processing the "outer" list and the other for generating the repetitions encoded in the inner sublists.
Notice that both procedures are mutually recursive (e.g., they call each other). The expand procedure recurs over the list, whereas the repeat procedure recurs over the number of repetitions. This is the general structure of the proposed solution, fill-in the blanks:
; input: lst - list to be processed
; output: list in the format requested
(define (expand lst)
(cond ((null? lst) ; if the list is null
'()) ; then return null
((not (pair? (car lst))) ; if the first element of the list is an atom
(cons <???> <???>)) ; cons the atom and advance the recursion
(else ; if the first element of the list is a list
<???>))) ; call `repeat` with the right params
; input: n - number of repetitions for the first element in the list
; lst - list, its first element is of the form (number atom)
; output: n repetitions of the atom in the first element of lst
(define (repeat n lst)
(if (zero? n) ; if the number of repetitions is zero
(expand (cdr lst)) ; continue with expand's recursion
(cons <???> ; else cons the atom in the first element and
<???>))) ; advance the recursion with one less repetition
As this was answered three years ago, I don't think that I am helping with homework. Would just like to point out that the two functions really don't need to be mutually recursive. As replicate is a fairly common function, I would propose:
(define (replicate what n)
(if (zero? n)
(list)
(cons what (replicate what (- n 1)))))
(define (my-expand xs)
(if (empty? xs)
(list)
(let ((x (first xs)))
(if (list? x)
(let ((the-number (first x))
(the-symbol (cadr x)))
(flatten (cons (replicate the-symbol the-number)
(my-expand (rest xs)))))
(cons x (my-expand (rest xs)))))))
Of course it is better to use two lists and perform the flatten at the end, something like this:
(define (my-expand xs)
(define (inner-expander xs ys)
(if (empty? xs) (flatten (reverse ys))
(let ((x (first xs)))
(if (list? x)
(let ((the-number (first x))
(the-symbol (cadr x)))
(inner-expander (rest xs) (cons (replicate the-symbol the-number) ys)))
(inner-expander (rest xs) (cons x ys))))))
(inner-expander xs (list)))