How to recursively reverse a list using only basic operations? - list

I was wondering how to reverse a list using only basic operations such as cons, first, rest, empty?, etc.
No helper functions or accumulators allowed, and the function only takes one input - a list.
I was told it was possible, though I can't wrap my head around it.
This is what I have conceptualized so far. I don't know how to form the recursion for the rest of the list.
(defunc rev-list (x)
(if (or (equal (len x) 0) (equal (len x) 1))
x
(cons (first (rev-list (rest x)))
???)))
Apparently it is possible to do something similar with a function that swaps the first and last of a list, though I don't fully understand it either. Here is the code for it:
(define swap-ends (x)
(if (or (equal (len x) 0) (equal (len x) 1))
x
(cons (first (swap-ends (rest x)))
(swap-ends (cons (first x)
(rest (swap-ends (rest x))))))))

(note: the answer is at the bottom of this post) The 2nd function,
(define (swap-ends x) ; swap [] = []
(if (or (equal (length x) 0) (equal (length x) 1)) ; swap [x] = [x]
x ; swap (x:xs)
(cons (first (swap-ends (rest x))) ; | (a:b) <- swap xs
(swap-ends (cons (first x) ; = a : swap (x : b)
(rest (swap-ends (rest x))))))))
(with Haskell translation in the comments) what does it do, you ask? The data flow diagram for if's alternative clause is
/-> first ----------------------> cons
x --> first ------/-------------> cons --> swap --/
\-> rest -> swap ---> rest ---/
(follow the arrows from left to right). So,
[] -> []
[1] -> [1]
/-> 2 -----------------------> [2,1]
[1,2] --> 1 --------/------------> [1] --> [1] --/
\-> [2] -> [2] ---> [] ---/
/-> 3 -------------------------> [3,2,1]
[1,2,3] --> 1 ------------/----------> [1,2] --> [2,1] --/
\-> [2,3] -> [3,2] -> [2] --/
/-----> 4 ----------------------------> [4,2,3,1]
[1,2,3,4] --> 1 ------------/---------------> [1,3,2] -> [2,3,1] -/
\-> [2,3,4] -> [4,3,2] -> [3,2] -/
So far it indeed does swap the end elements of a list. Let's prove it by the natural induction,
true(N-1) => true(N):
/-> N --------------------------------------> [N,2..N-1,1]
[1..N] --> 1 ---------/-----------> [1,3..N-1,2] -> [2,3..N-1,1] -/
\-> [2..N] -> [N,3..N-1,2] /
-> [3..N-1,2] -/
So it is proven. Thus, we need to devise a data flow diagram which, under the supposition of reversing an (N-1)-length list, will reverse an N-length list:
[1..N] --> 1 ------------------------------------\
\-> [2..N] -> [N,N-1..2] -> N -------------\------------------\
\-> [N-1,N-2..2] -> [2..N-1] -> [1..N-1] -> rev -> cons
Which gives us the implementation
(define (rev ls) ; rev [] = []
(cond ; rev [x] = [x]
((null? ls) ls) ; rev (x:xs)
((null? (rest ls)) ls) ; | (a:b) <- rev xs
(else ; = a : rev (x : rev b)
(cons (first (rev (rest ls)))
(rev (cons (first ls)
(rev (rest (rev (rest ls))))))))))
(rev '(1 2 3 4 5)) ; testing
;Value 13: (5 4 3 2 1)
The Haskell translation in the comments follows the diagram quite naturally. It is actually readable: a is the last element, b is the reversed "core" (i.e. the input list without its first and last element), so we reverse the reversed core, prepend the first element to get the butlast part of the input list, then reverse it and prepend the last element. Simple. :)
2020 update: here's a Scheme version based on the code by #Rörd from the comments, such that it is similarly readable, with arguments destructuring in place of Haskell's pattern matching:
(define (bind lst fun)
(apply fun lst))
(define (rev lst)
(if (or (null? lst)
(null? (cdr lst)))
lst
(bind lst
(lambda (first . rest)
(bind (rev rest)
(lambda (last . revd-core)
(cons last (rev (cons first (rev revd-core))))))))))

(define (reverse x)
(let loop ((x x) (y '()))
(if (null? x)
y
(let ((temp (cdr x)))
(set-cdr! x y)
(loop temp x))))))
Really one of the few ways to do it efficiently. But still sort of a helper procedure.
Other way, but not tail-recursive, and if the append doesn't use a set-cdr! it's really unusable for large lists.
(define (reverse L)
(if (null? l)
'()
(append (reverse (cdr L)) (list (car L)))))

Do you have last and butlast in your environment? If so, the procedure can be defined like this (though as Oscar notes this isn't how you'd normally want to approach the problem):
(define (rev lst)
(if (null? lst)
'()
(cons (car (last lst))
(rev (butlast lst)))))
Here are definitions of last and butlast. It sounds like they won't do you any good for this assignment if they're not part of your default environment, but when you're starting out it's good to read through and think about lots of recursive procedures.
(define (butlast lst)
(if (or (null? lst) (null? (cdr lst)))
'()
(cons (car lst) (butlast (cdr lst)))))
(define (last lst)
(if (or (null? lst) (null? (cdr lst)))
lst
(last (cdr lst))))

Related

Return a list without the last element

I've just started to learn Racket.
I have this code:
#lang racket
(define l1 '(1 2 3 4))
(car l1)
(cdr l1)
(car l1) returns 1.
(cdr l1) returns '(2 3 4)
Is there a function that returns '(1 2 3)?
I've tried this:
#lang racket
(define l1 '(1 2 3 4))
(map
(lambda (l i)
(if (not (= i (sub1 (length l1)))) l '()))
l1 (range 0 (length l1)))
But, it returns: '(1 2 3 ())
And I have also tried:
#lang racket
(define l1 '(1 2 3 4))
(map
(lambda (l i)
(cond ((not (= i (sub1 (length l1)))) l )))
l1 (range 0 (length l1)))
But, it returns: '(1 2 3 #<void>)
The map function always returns a list the same length as its input. You want an output list that is shorter than its input. The function you are looking for is traditionally called but-last:
(define (but-last xs) (reverse (cdr (reverse xs))))
What about something like this ?
(define (myCdr l)
(if (not (pair? (cdr l)))
'()
(cons (car l) (myCdr (cdr l)))
)
)
length is generally an anti-pattern in Scheme because the entire list needs to be read in order to get the result. W. Ness remarks that map does not alter the structure of the list, and the behavior of filter is based on the list's values, neither of which suit your needs.
Instead of making potentially expensive computations first or awkwardly applying the library functions, you can compute the init of a list using direct recursion -
(define (init l)
(cond ((null? l)
(error 'init "cannot get init of empty list"))
((null? (cdr l))
null)
(else
(cons (car l)
(init (cdr l))))))
(init '(a b c d e)) ;; '(a b c d)
(init '(a)) ;; '(a)
(init '()) ;; init: cannot get init of empty list
Or a tail-recursive form that only uses one reverse -
(define (init l)
(let loop ((acc null)
(l l))
(cond ((null? l)
(error 'init "cannot get init of empty list"))
((null? (cdr l))
(reverse acc))
(else
(loop (cons (car l) acc)
(cdr l))))))
(init '(a b c d e)) ;; '(a b c d)
(init '(a)) ;; '(a)
(init '()) ;; init: cannot get init of empty list
And lastly a tail-recursive form that does not use length or reverse. For more intuition on how this works, see "How do collector functions work in Scheme?" -
(define (init l (return identity))
(cond ((null? l)
(error 'init "cannot get init of empty list"))
((null? (cdr l))
(return null))
(else
(init (cdr l)
(lambda (r)
(return (cons (car l) r)))))))
(init '(a b c d e)) ;; '(a b c d)
(init '(a)) ;; '(a)
(init '()) ;; init: cannot get init of empty list
Here's one more, via zipping:
#lang racket
(require srfi/1)
(define (but-last-zip xs)
(if (null xs)
xs ; or error, you choose
(map (lambda (x y) x)
xs
(cdr xs))))
Here's another, emulating filtering via lists with appending, where empty lists disappear by themselves:
(define (but-last-app xs)
(if (null? xs)
xs
(let ((n (length xs)))
(apply append ; the magic
(map (lambda (x i)
(if (= i (- n 1)) '() (list x)))
xs
(range n))))))
Or we could use the decorate--filter--undecorate directly, it's even more code!
(define (but-last-fil xs)
(if (null? xs)
xs
(let ((n (length xs)))
(map car
(filter (lambda (x) (not (null? x)))
(map (lambda (x i)
(if (= i (- n 1)) '() (list x)))
xs
(range n)))))))
Here's yet another alternative, assuming that the list is non-empty. It's efficient (it performs a single pass over the list), and it doesn't get any simpler than this!
(define (delete-last lst)
(drop-right lst 1))
(delete-last '(1 2 3 4))
=> '(1 2 3)
Here is an equivalent of Will Ness's beautiful but-last-zip which does not rely on srfi/1 in Racket: without srfi/1 Racket's map insists that all its arguments are the same length (as does the R5RS version in fact) but it is common in other Lisps to have the function terminate at the end of the shortest list.
This function uses Racket's for/list and also wires in the assumption that the result for the empty list is the empty list.
#lang racket
(define (but-last-zip xs)
(for/list ([x xs] [y (if (null? xs) xs (rest xs))])
x))
I think Will's version is purer: mapping functions over things is a very Lisp thing to do I think, while for/list feels less Lispy to me. This version's only advantage is that it does not require a module.
My own solution using recursion:
#lang racket
(define but-last
(lambda (l)
(cond ((null? (cdr l)) '())
(else (cons (car l) (but-last (cdr l)))))))
And another solution using filter-not and map:
#lang racket
(define l1 '(1 2 3 4))
(filter-not empty? (map
(lambda (l i)
(if (not (= i (sub1 (length l1)))) l empty))
l1 (range 0 (length l1))))

Racket exchanging first and third elements of a list in a function?

Suppose, I got a list(s) in Racket of:
`(1 2 3)
`(1 2 3 4)
`((1 2) (3) (4)))
Then when I exchange it (first and third element), it will look like:
`(3 2 1)
`(3 2 1 4)
`((4) (3) (1 2))
Please note: I may only use things such as cons, first, and rest for this.
Here's my current try:
(define new-list
(lambda(list)
(first (rest (rest list))))) ; Get third element.
I cannot replicate the results above. I would like to be shown how it's done.
Just play with it a little:
> '(one)
'(one)
> '(one two three)
'(one two three)
> (first '(one two three four))
'one
> (rest '(one two three four))
'(two three four)
> (define foo (lambda (lst)
(cons (first lst) (rest lst))))
> (foo '(one two three four))
'(one two three four)
> (define bar (lambda (lst)
(cons (first (rest lst))
(cons (first lst) (rest lst)))))
> (bar '(one two three four))
'(two one two three four)
Now you have everything to complete the process.
To write your procedure, we want to take an input list xs, and construct a new list like
(cons (third-element xs)
(cons (second-element xs)
(cons (first-element xs)
(everything-except-the-first-three-elements xs)))
In racket you have car and cdr, but you also have cadr, caddr, cddddr and everything in betweeen.
To get an intuition for them, a reads the head, d reads the tail, so
car gets the head (the first element in a list)
cdr gets the tail (everything except the first element in a list)
cadr gets the head of the tail (the second element in a list)
cddr gets the tail of the tail (everything except the first and second elements in a list)
caddr gets the head of the tail of the tail (the third element in a list)
and so on ...
We can write this easily using our car and cdr helpers
(define (swap xs)
(cons (caddr xs) ; cons the third element ...
(cons (cadr xs) ; onto the second element ...
(cons (car xs) ; onto the first element ...
(cdddr xs))))) ; onto the tail of the third element
(swap '(1 2 3)) ; '(3 2 1)
(swap '(1 2 3 4)) ; '(3 2 1 4)
(swap '((1 2) (3) (4))) ; '((4) (3) (1 2))
(define nth
;; Return nth element of list
(lambda (lst n)
(cond ((null? lst) 'nil)
((zero? n) (car lst))
(else (nth (cdr lst) (- n 1))))))
(define slice
;; Return lst sliced like python
(lambda (lst a b acc)
(cond ((null? lst) (reverse acc))
((< b a) (reverse acc))
((zero? b) (reverse acc))
((zero? a) (slice (cdr lst) 0 (- b 1) (cons (car lst) acc)))
(else (slice (cdr lst) (- a 1) (- b 1) acc)))))
(define swap
;; Return nth and mth element swapped list
(lambda (lst n m)
`(,#(slice lst 0 n '())
,(nth lst m)
,#(slice lst (+ n 1) m '())
,(nth lst n)
,#(slice lst (+ m 1) (length lst) '()))))
This swaps any two given positions m and n and returns the resulting list.
(swap your-list 0 2) ; returns your-list 1st and 3rd elements swapped

How to work with specific range of numbers in lists in scheme?

I'm learning scheme and I would like to know how to remove, for example the negative numbers of a list so I can work with the positives, until now I only got # instead of removing the negatives.
Here is my code:
(define test
(lambda (list)
(map (lambda (x) (if (> x 0) x ))list)))
Here is the standard version of filter:
(define (filter pred? xs)
(let loop ((xs xs) (ys '()))
(cond ((null? xs) (reverse ys))
((pred? (car xs))
(loop (cdr xs) (cons (car xs) ys)))
(else (loop (cdr xs) ys)))))
With that, you can build a new list containing only the positive values of the input list:
> (filter positive? '(3 9 -2 4 0 -1 7))
(3 9 4 7)
You might enjoy my blog, which provides lots of Scheme code for you to study.
I turned the negatives of the list into 0 so it doesn't affect what I do.
Here's the code for it:
(define test
(lambda(list)
(map (lambda (x)(if (> x 0) x
0 ))list) ) )
You will need to disect and rebuild the list element by element. This is a very common recursive pattern in Scheme and should be covered in depth in your course material.
(define (filter list pred)
(cond ((null? list)
'())
((pred (car list))
(filter (cdr list) pred))
(else
(cons (car list) (filter (cdr list) pred)))))

Transpose list of tuples filling with empty lists

I'm new to Scheme and I'm trying to write a procedure which combines n list into a list of n-tuples. If the lists are of different size, the tuples should contain the empty list () when the corresponding list ran out of elements.
My current implementation is the following:
(define (comb list1 list2)
(cond [(empty? list1) empty]
[(empty? list2) empty]
[else (cons (list (first list1) (first list2))
(comb (rest list1) (rest list2)))]))
However, this program doesn't produce another tuple when there are no more items in the list to combine. For instance, (comb '(1 2 3 ) '(3 4)) produces only ((1 3) (2 4))
How do I solve it?
This is a bit tricky, and I believe it's not an appropriate exercise for someone who is just learning the basics of the language. Anyway, here's my proposed solution, in terms of higher-order procedures:
; helper procedure for filling a list with arbitrary values at the end
(define (fill lst val num)
(append lst
(build-list num (const val))))
; helper procedure for transposing a list of lists
(define (transpose lsts)
(apply map list lsts))
; main procedure
(define (list-tuples lsts)
(let* ((lengths (map length lsts)) ; obtain the length of each sublist
(max-length (apply max lengths))) ; find out the maximum length
(transpose ; build new sublists element-wise
(map (lambda (lst len) ; build sublists of the right length
(fill lst '() (- max-length len))) ; fill sublists with '()
lsts
lengths))))
The trick was to find the maximum length of the lists and then build new lists with that length, filling them with '() at the end. After that, it's a simple matter of building the answer by taking one element from each sublist. It works as expected:
(list-tuples '((m n o) (1) (x y)))
=> '((m 1 x) (n () y) (o () ()))
You need to specifically deal with the situation where one of the lists is empty. The following does what I think you want with two lists.
(define (comb l1 l2)
(cond
((empty? l1)
(cond
((empty? l2) '())
(else (cons (list '() (car l2)) (comb l1 (cdr l2))))))
(else
(cond
((empty? l2) (cons (list (car l1) '()) (comb (cdr l1) l2)))
(else (cons (list (car l1) (car l2)) (comb (cdr l1) (cdr l2))))))))
Let's split the problem into 2 parts.
First let's assume a procedure that will take a list, and return the following results:
a list containing the first items of each sublist
a list containing the remainder of each sublist
the number of non-empty lists encountered
An example implementation could be:
(define (split-tuples lst)
(let loop ((lst lst) (fst null) (rst null) (cnt 0))
(if (null? lst)
(values (reverse fst) (reverse rst) cnt)
(let ((c (car lst)))
(if (null? c)
(loop (cdr lst) (cons c fst) (cons c rst) cnt)
(loop (cdr lst) (cons (car c) fst) (cons (cdr c) rst) (add1 cnt)))))))
Testing:
> (split-tuples '((m n o) (1) (x y)))
'(m 1 x)
'((n o) () (y))
3
> (split-tuples '((n o) () (y)))
'(n () y)
'((o) () ())
2
> (split-tuples '((o) () ()))
'(o () ())
'(() () ())
1
> (split-tuples '(() () ()))
'(() () ())
'(() () ())
0
Now using this procedure we create the main procedure that will just loop until all sublists are empty:
(define (list-tuples lst)
(let loop ((lst lst) (res null))
(let-values (((fst rst cnt) (split-tuples lst)))
(if (zero? cnt)
(reverse res)
(loop rst (cons fst res))))))
Testing:
> (list-tuples '((m n o) (1) (x y)))
'((m 1 x) (n () y) (o () ()))
> (list-tuples '())
'()

Add or remove element in the middle of list (Scheme)

Is it possible to add or remove elements in the middle of a linked list in Scheme? I can't seem to think of a way doing this with car/cdr/cons, but I reccon there must be a way to this.
If I have a list '(1 3 5 6), and I need to put in 4 between 5 and 6 for example, is this doable?
Adding an element at a given position was done in a previous answer. Removing an element at a given position is similar:
(define (delete-at k lst)
(cond ((null? lst)
'())
((zero? k)
(cdr lst))
(else
(cons (car lst)
(delete-at (sub1 k) (cdr lst))))))
Here are two versions:
; list-insert : value index list -> list
; return list where the ith element is x
(define (list-insert x i xs)
(if (= i 0)
(cons x xs)
(cons (first xs) (list-insert x (- i 1) (rest xs)))))
(define (list-insert/version2 x i xs)
(define-values (before after) (split-at xs i))
(append before (list x) after))
(list-insert/version2 'x 2 '(a b c d))
(list-insert 'x 2 '(a b c d))
Both versions will allocate a new list. For large lists it will become inefficient.