I would like to write a Racket function that takes a list and returns the position of the smallest element of that list. I already wrote a function that works:
(define (min-position xs)
(define (min-position2 count pos xs)
(cond ((null? xs) #f)
((= 1 (length xs)) pos)
((< (car xs) (cadr xs))
(min-position2 (+ count 1) pos (cons (car xs) (cddr xs))))
(else (min-position2 0 (+ count pos 1) (cons (cadr xs) (cddr xs))))))
(min-position2 0 0 xs))
Example inputs and outputs:
> (min-position '(9 8 7 6 5))
4
> (min-position '(9 8 1 6 5))
2
> (min-position '(0 1 2))
0
But is there a more elegant way to write this?
I'm not sure what you mean by "elegant". There may be a spiffier algorithm, for example. But here's how I would make the code more readable (IMHO) while retaining your basic approach.
Step by step:
Your input/ouput examples, rewritten as check-equal? tests:
#lang racket
(require rackunit)
(define (test f)
(check-equal? (f '(9 8 7 6 5)) 4)
(check-equal? (f '(9 8 1 6 5)) 2)
(check-equal? (f '(0 1)) 0)
(check-equal? (f '(0 1 2)) 0))
Your original, but using [] instead of () for cond clauses.
(define (min-position/v0 xs)
(define (min-position2 count pos xs)
(cond [(null? xs) #f]
[(= 1 (length xs)) pos]
[(< (car xs) (cadr xs))
(min-position2 (+ count 1) pos (cons (car xs) (cddr xs)))]
[else
(min-position2 0 (+ count pos 1) (cons (cadr xs) (cddr xs)))]))
(min-position2 0 0 xs))
(test min-position/v0)
Using match to destructure the list and use names like this and next instead of (car xs) and (cadr xs):
(define (min-position/match xs)
(define (min-position2 count pos xs)
(match xs
[(list) #f]
[(list _) pos]
[(list this next more ...)
(cond [(< this next)
(min-position2 (+ count 1) pos (cons this more))]
[else
(min-position2 0 (+ count pos 1) (cons next more))])]))
(min-position2 0 0 xs))
(test min-position/match)
Changing the internal function to a let loop .... Really the same thing, just a bit more concise.
(define (min-position/match&loop xs)
(let loop ([count 0] [pos 0] [xs xs])
(match xs
[(list) #f]
[(list _) pos]
[(list this next more ...)
(cond [(< this next) (loop (+ count 1) pos (cons this more))]
[else (loop 0 (+ count pos 1) (cons next more))])])))
(test min-position/match&loop)
Again, this is the same algorithm as your original. But I would find it easier to grok quickly.
Well, it all depends on your definition of elegant. For me, an elegant solution is one that is short, clear, idiomatic and uses existing procedures (that is, it doesn't reinvent the wheel). Here's my shot:
(require srfi/1) ; import `list-index`
(require srfi/26) ; import `cute`
(define (min-position lst)
(and (not (null? lst))
(list-index (cute = (apply min lst) <>) lst)))
Here's how it works:
(apply min lst) finds the minimum element in the list, using the built-in min procedure
(cute = (apply min lst) <>) uses cute for creating a specialized predicate that will return #t whenever an element is equal to the minimum, making sure that we find the minimum only once
(list-index (cute = (apply min lst) <>) lst) uses list-index with the previous predicate to find the index with the first minimum element in the list
The (and (not (null? lst)) … ) part is there for handling the edge case where the input list is empty
Short and sweet. The only disadvantage is that it traverses the input list twice, once for finding the minimum element and once again for finding the index of that element. But that's a small price to pay, and it's still an O(n) solution that works as expected:
(min-position '(9 8 7 6 5))
=> 4
(min-position '(9 8 1 6 5))
=> 2
(min-position '(0 1 2))
=> 0
(min-position '())
=> #f
A named let is a common idiom in Scheme:
(define (min-position xs)
(let loop ((xs xs) (pos 0) (mn #f) (mnpos #f))
(if (null? xs)
mnpos
(let ((c (car xs)))
(if (or (not mn) (< c mn))
(loop (cdr xs) (add1 pos) c pos)
(loop (cdr xs) (add1 pos) mn mnpos))))))
In Racket, you also have for/fold and in-indexed to make the code even shorter:
(define (min-position xs)
(define-values (mn mnpos)
(for/fold ((mn #f) (mnpos #f)) (((c pos) (in-indexed xs)))
(if (or (not mn) (< c mn))
(values c pos)
(values mn mnpos))))
mnpos)
Related
Im trying to write a small program that will take a list as an input and the first function is supposed to remove values that are greater than 4 and the other one is supposed to remove values that are lower than 4 in the check-expects. I figure this is a simple solution but I cant seem to figure it out as Im still new to lists and data structures. Any help would be appreciated, here is the code:
(define (removehigher lon n)
(cond [(> n lon)(remove (lon))]
[(< n lon) true]))
(define (removelower lon n)
(cond [(> n lon) true]
[(< n lon) (remove(lon))]))
(check-expect(removehigher(list 0 1 2 3 4 5 6)4)(list 0 1 2 3))
(check-expect(removelower(list 0 1 2 5 6 7) 3)(list 5 6 7))
There is a function called filter which would do this.
(define (my-filter pred lst (acc '()))
(cond [(null? lst) (reverse acc)]
[(pred (car lst)) (my-filter pred (cdr lst) (cons (car lst) acc))]
[else (my-filter pred (cdr lst) acc)]))
It is actually an in-built function filter. I use my- as prefix to not to overwrite this in-built function.
Once you have the function filter (or my-filter), you can write your desired functions easily - it is then about to define the predicate function (pred) correctly for each of the cases:
(define (remove-higher lst n)
(filter (lambda (x) (<= x n)) lst))
(define (remove-lower lst n)
(filter (lambda (x) (<= n x)) lst))
Alternatively, one could also use append-map:
(define (remove-higher lst n)
(append-map (lambda (x) (if (<= x n) (list x) '())) lst))
(define (remove-lower lst n)
(append-map (lambda (x) (if (<= n x) (list x) '())) lst))
Or define filter using append-map:
(define (my-filter pred lst)
(append-map (lambda (x) (if (pred x) (list x) '())) lst))
append-map in turn can be defined:
(define (my-append-map func lst)
(apply append (map func lst)))
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))))
I am currently trying to write a function that gives me a list with the position numbers of the position that contains the element 1 as elements.
Unfortunately, when I execute the following function it gives me '()which is the initial value of lst.
So I am not sure what to write for the case (< pos 0).
(define lst '())
(define (posj pos) (if (< pos 0)
lst
(begin (cond ((equal? (list-ref l pos) 1) (begin (set! lst (append (list* pos) '()))
(posj (- pos 1))))
(else (posj (- pos 1)))))))
(define l '(1 3 1 2 1 5 1))
(posj (- (length l) 1))
What you probably meant to write was this, with list instead of list*, lst instead of '(): (and with more standard indentation)
(define lst '())
(define (posj pos)
(if (< pos 0)
lst
(cond [(equal? (list-ref l pos) 1)
(begin
(set! lst (append (list pos) lst))
(posj (- pos 1)))]
[else
(posj (- pos 1))])))
(define l '(1 3 1 2 1 5 1))
(posj (- (length l) 1))
That works, but it relies on global variables (l and lst), which isn't a very good way of doing things. Then you can translate that from global variables into function arguments. The (set! lst (append ...)) can be replaced by passing the (append ...) as an argument:
(define (posj l lst pos)
(if (< pos 0)
lst
(cond [(equal? (list-ref l pos) 1)
(posj l (append (list pos) lst) (- pos 1))]
[else
(posj l lst (- pos 1))])))
(define l '(1 3 1 2 1 5 1))
(posj l '() (- (length l) 1))
So far l has just been the same list every time, when the part that you actually need shrinks every time. To fix that, you can iterate from front-to-back instead of back-front, and use first and rest in the recursion. Also the order of the arguments to append has to be switched because we're iterating in the other direction now, and the (< pos 0) check can be replaced by an (empty? l) check:
(define (posj l lst pos)
(if (empty? l)
lst
(cond [(equal? (first l) 1)
(posj (rest l) (append lst (list pos)) (+ pos 1))]
[else
(posj (rest l) lst (+ pos 1))])))
(define l '(1 3 1 2 1 5 1))
(posj l '() 0)
Now instead of using append it's much more efficient if you can use cons instead. That reverses lst, so rename it to rev-lst, and when you return it, reverse it again:
(define (posj l rev-lst pos)
(if (empty? l)
(reverse rev-lst)
(cond [(equal? (first l) 1)
(posj (rest l) (cons pos rev-lst) (+ pos 1))]
[else
(posj (rest l) rev-lst (+ pos 1))])))
(define l '(1 3 1 2 1 5 1))
(posj l '() 0)
Now it's starting to look a lot like the template for list functions, especially if you replace that if with a cond. Also, if you wanted to avoid passing '() and 0 as extra arguments, you could use either a helper function or a named let.
Your code doesn't work (append expects only lists) and is not the way to do it in Scheme for more than one reason.
I would suggest using a named let (classic Scheme):
(define (posj lst val)
(let loop ((lst lst) (pos 0) (res null))
(if (null? lst)
(reverse res)
(loop (cdr lst)
(add1 pos)
(if (= (car lst) val) (cons pos res) res)))))
or the more "rackety"
(define (posj lst val)
(reverse
(for/fold ((res null)) (((elt pos) (in-indexed lst)))
(if (= elt val) (cons pos res) res))))
then
> (posj '(1 3 1 2 1 5 1) 1)
'(0 2 4 6)
Since you're using Racket and presumably Dr Racket (the IDE),
use the automatic indentation feature, it helps a lot to make your code readable
in order to understand how this code works, use the build-in debugger to execute it step by step
read a Scheme tutorial which will teach you the common looping constructs, among other useful things.
Trying to learn lisp, want to delete every nth. I only managed to delete the first (nth) element
(defun delete-nth (n list)
(if (zerop n)
(cdr list)
(let ((cons (nthcdr (1- n) list)))
(if cons
(setf (cdr cons) (cddr cons))
cons))))
I'd like to delete the next nth and so on
Also I tried this:
(defun remove-nth (list n)
(remove-if (constantly t) list :start n :end (+ 1 n)))
No idea how to start again
What I was thinking was concatenating, but I have no idea of how to keep track of my position.
Counting from 1 (changing to 0 is trivial):
(defun remove-every-nth (n list)
(loop for element in list
for index from 1
unless (zerop (rem index n))
collect element))
Also: Please indent your code correctly.
An alternative way to do the same thing:
(defun remove-all-nth (list period)
(remove-if
(let ((iterator 0))
(lambda (x)
(declare (ignore x))
(= 0 (mod (incf iterator) period)))) list))
(remove-all-nth '(1 2 3 4 5 6 7 8 9 0) 3)
; (1 2 4 5 7 8 0)
Perhaps a more academic recursive solution here:
(defun delete-nth (n list)
(labels ((rec (i list)
(cond ((null list) nil)
((= i 1) (rec n (cdr list)))
(t (cons (car list) (rec (1- i) (cdr list)))))))
(rec n list)))
But in real life I'd use the loop option above.
I'm new to scheme and am having some trouble debugging my code.
; returns number of elements in a list
(define (length L)
(cond ((null? L) 0)
(else (+ (length (cdr L)) 1))))
; split the list in half:
; returns ((first half)(second half))
(define (split L)
(cond
((= (length L) 0) (list L L) )
((= (length L) 1) (list L '() ))
(else
(list (sublist L 1 (/ (length L) 2) 1)
(sublist L (+ (/ (length L) 2) 1) (length L) 1)))))
; extract elements start to end into a list
(define (sublist L start end counter)
(cond ((null? L) L)
((< counter start) (sublist (cdr L) start end (+ counter 1)))
((> counter end) '())
(else (cons (car L) (sublist (cdr L) start end (+ counter 1))))))
To me, this feels like it would split a single list into two sub lists. There may be an easier way to do this, and so I apologize if this seems cumbersome.
Anyway, the results:
Expected: (split '(1 2 3 4 5)) = ('(1 2) '(3 4 5))
Actual: (split '(1 2 3 4 5)) = ('(1 2) '(4 5))
It's clear that the length or split is losing the middle value, but I've checked it again and again and it seems to lose the middle value. It seems like an easy solution would be to get rid of the (+ 1) of (+ (/ (length L) 2) 1) but this seems counter intuitive to me, as:
Assume L = '(1 2 3 4 5), (/ (length L) 2) = 2, and (+ (/ (length L) 2) 1) = 3
(sublist L 1 (2) 1) = '(1 2)
(sublist L (3) 5 1) = '(3 4 5)
** I put parens around the 2 and 3 to indicate that they were length calculations.
Clearly an assumption I am making is false, or I am overlooking something trivial.
Thanks in advance!
Do you know the tortoise-and-hare algorithm? The tortoise walks the list, the hare runs the list at double speed. The split occurs at the position of the tortoise when the hare reaches the end of the list. Here's most of the code; I'll let you figure out the rest:
(define (split xs)
(let loop ((ts xs) (hs xs) (zs (list)))
(if (or (null? hs) (null? (cdr hs)))
(values (reverse zs) ts)
(loop ...))))
Here ts is the remaining list of items to be examined by the tortoise, hs is the remaining list of items to be examined by the hare, and zs is the list of items already examined by the tortoise, in reverse order.
Note that you never need to count the items in the input list.
I'm not going to debug your code for you. Instead, here's a simpler definition of split:
(define (split l)
(let ((n (length l)))
(list (take (/ n 2) l)
(drop (+ (/ n 2) (mod n 2)) l))))
Exercise for the reader: implement take and drop. The latter is just recursion on n while taking the cdr of l in the recursive case; the former takes slightly more effort to get right in the base case (stopping condition).
Here is almost your solution (Racket Scheme):
#lang racket
(define (length lst)
(cond [(empty? lst) 0]
[else (+ (length (rest lst)) 1)]))
(define (first-length lst)
(quotient (length lst) 2))
(define (second-length lst)
(- (length lst) (first-length lst)))
(define (sublist lst start end counter)
(cond [(empty? lst) lst]
[(< counter start) (sublist (rest lst) start end (+ counter 1))]
[(> counter end) '()]
[else (cons (first lst) (sublist (rest lst) start end (+ counter 1)))]))
(define (first-half-of-list lst)
(sublist lst 1 (first-length lst) 1))
(define (second-half-of-list lst)
(sublist lst (second-length lst) (length lst) 1))
(define (split lst)
(cond
[(= (length lst) 0) (list lst lst)]
[(= (length lst) 1) (list lst '())]
[else (list (first-half-of-list lst)
(second-half-of-list lst))]))
(split '(1 2 3 4 5))
=> '((1 2) (3 4 5))
Thank you for good brain exercise.
The key moment is 'second-length'-function.