Remove elements in a list using a pattern - list

Greeting everyone. I'm trying to write an algorithm in Racket but I'm faced with a problem:
I'm studying way of generating different types of grids over surfaces, using a CAD software as a backend for Racket. Basically I have a function that generates a matrix of point coordinates (in the u and v domains) of a parametric surface and another one which connects those points with a line, in a certain order, to create the grid pattern. The problem is, to obtain more complex grids I need to be able to remove certain points from that matrix.
With that said, I have a list of data (points in my case) and I want to remove items from that list based on a true-false-false-true pattern. For example, given the list '(0 1 2 3 4 5 6 7 8 9 10) the algorithm would keep the first element, remove the next two, keep the third and then iterate the same patter for the rest of the list, returning as the final result the list '(0 3 4 7 8).
Any suggestions? Thank you.

Using Racket's for loops:
(define (pattern-filter pat lst)
(reverse
(for/fold ((res null)) ((p (in-cycle pat)) (e (in-list lst)))
(if p (cons e res) res))))
testing
> (pattern-filter '(#t #f #f #t) '(0 1 2 3 4 5 6 7 8 9 10))
'(0 3 4 7 8)

A solution using list functions in SRFI-1:
#!racket
(require srfi/1)
(define (pattern-filter pat lst)
(fold-right (λ (p e acc) (if p (cons e acc) acc))
'()
(apply circular-list pat)
lst))
(pattern-filter '(#t #f #f #t)
'(0 1 2 3 4 5 6 7 8 9 10)) ; ==> '(0 3 4 7 8)
There are other ways but it won't become easier to read.

In Racket I would probably use match to express the specific pattern you described:
#lang racket
(define (f xs)
(match xs
[(list* a _ _ d more) (list* a d (f more))]
[(cons a _) (list a)]
[_ (list)]))
(require rackunit)
;; Your example:
(check-equal? (f '(0 1 2 3 4 5 6 7 8 9 10)) '(0 3 4 7 8))
;; Other tests:
(check-equal? (f '()) '())
(check-equal? (f '(0)) '(0))
(check-equal? (f '(0 1)) '(0))
(check-equal? (f '(0 1 2)) '(0))
(check-equal? (f '(0 1 2 3)) '(0 3))
(check-equal? (f '(0 1 2 3 4)) '(0 3 4))
However I also like (and upvoted) both usepla's and Sylwester's answers because they generalize the pattern.
Update: My original example used (list a _ _ d more ...) and (list a _ ...) match patterns. But that's slow! Instead use (list* a _ _ d more) and (cons a _), respectively. That expands to the sort of fast code you'd write manually with cond and list primitives.

The question is tagged with both scheme and racket, so it's probably not a bad idea to have an implementation that works in Scheme in addition to the versions that work for Racket given in some of the other answers. This uses the same type of approach that's seen in some of those other answers: create an infinite repetition of your boolean pattern and iterate down it and the input list, keeping the elements where your pattern is true.
Here's a method that takes a list of elements and a list of #t and #f, and returns a list of the elements that were at the same position as #t in the pattern. It ends whenever elements or pattern has no more elements.
(define (keep elements pattern)
;; Simple implementation, non-tail recursive
(if (or (null? elements)
(null? pattern))
'()
(let ((tail (keep (cdr elements) (cdr pattern))))
(if (car pattern)
(cons (car elements) tail)
tail))))
(define (keep elements pattern)
;; Tail recursive version with accumulator and final reverse
(let keep ((elements elements)
(pattern pattern)
(result '()))
(if (or (null? elements)
(null? pattern))
(reverse result)
(keep (cdr elements)
(cdr pattern)
(if (car pattern)
(cons (car elements) result)
result)))))
To get an appropriate repeating pattern, we can create a circular list of the form (#t #f #f #t …) we can create a list (#t #f #f #t) and then destructively concatenate it with itself using nconc. (I called it nconc because I've got a Common Lisp background. In Scheme, it's probably more idiomatic to call it append!.)
(define (nconc x y)
(if (null? x) y
(let advance ((tail x))
(cond
((null? (cdr tail))
(set-cdr! tail y)
x)
(else
(advance (cdr tail)))))))
(let ((a (list 1 2 3)))
(nconc a a))
;=> #0=(1 2 3 . #0#)
Thus, we have a solution:
(let ((patt (list #t #f #f #t)))
(keep '(0 1 2 3 4 5 6 7 8 9 0) (nconc patt patt)))
;=> (0 3 4 7 8)

Related

Racket lang: access element in list

Give a single function to access the element a in the list L.
(define L '(1 2 (a 3 4 5)))
Following the form (define id expr) which binds id to the result of expression I have tried the following:
(define L '(1 2 (a 3 4 5) (car(cdr L))))
cdr accesses the tail of the list, i.e. a 3 4 5, if I am not mistaken, and then I apply car on the tail to access the head of the list, i.e a. However, it is not working on DrRacket IDE.
I think you meant to do this:
(define L '(1 2 (a 3 4 5)))
(car (car (cdr (cdr L))))
=> 'a
Which can also be written as:
(caaddr L)
=> 'a
You included the (car(cdr L)) part inside the list L.
> (define L '(1 2 (a 3 4 5) (car(cdr L))))
> L
(list 1 2 (list 'a 3 4 5) (list 'car (list 'cdr 'L))) ;; oh no
But that still doesn't extract the 'a because you need to access car of the inner list:
(define L '(1 2 (a 3 4 5)))
(car (car (cdr (cdr L))))
;; or (caaddr L)

Select certain items from a list in Racket

Let's say I want to get all the even numbers from a list in racket, I would do something like this:
(define (even lst)
(map (λ(x)
(if (even? x) (append x) (append '()) )) lst))
When I use the input (even '(1 2 3 4)) what I actually get is '( () 2 () 4) instead of '(2 4), which is the desired output.
Is there a way I can do this?
Here are two solutions:
#lang racket
(define (keep-even xs)
(match xs
;; pattern template
[(cons (? even? x0) xs) (cons x0 (keep-even xs))]
[(cons x0 xs) (keep-even xs)]
['() '()]))
(keep-even '(1 2 3 4 5 6 7 8))
The pattern is common, so the standard library has filter:
(filter even? '(1 2 3 4 5 6 7 8))

First 5 elements of a list in Scheme

I was wondering, if I was given a list such as:
(list 3 6 9 2 1 0 5 9)
and I wanted to produce only the first 5. i.e.: I want to produce:
(list 3 6 9 2 1)
How could I go about doing this. By the way, recursion is not allowed, and the language is intermediate student. Thanks :)
Actually a lis like (1 2 3 4) is a chain of pairs (1 . (2 . (3 . (4 . ())))). You cannot reuse the pairs since you need the 5th pair to point to () (null) instead of the rest of the pair chain. The only way to do this is to make a new pair for each element you'd like by using the same car elements in each.
(define (take lst cnt)
(if (zero? cnt) ; if you ask for zero element
'() ; then return empty list
(cons (car lst) ; else make a pair with first element
(take (cdr lst) ; and result from take with the rest of the list
(- cnt 1))))) ; with one less element than you originally asked for
If memory serves, in addition to car and cdr, Scheme supplies caar, caaar, caaaar and the equivalent repeated ds and the various combinations and permutations. So one solution would be:
(define x (list 3 6 9 2 1 0 5 9))
(list (car x) (cadr x) (caddr x) (cadddr x) (car (cddddr x)))
(and I'm sure that's why you were asked for the first five; there's no cdddddr — the teaching point was likely the permitted repetition of ds and as and the limit to which you can go with those)
I do not know why you would want to do this, but one way to avoid recursion is to unroll the loop:
(define (take1 xs) (cons (car xs) '()))
(define (take2 xs)
(cons (car xs) (take1 (cdr xs))))
(define (take3 xs)
(cons (car xs) (take2 (cdr xs))))
(define (take4 xs)
(cons (car xs) (take3 (cdr xs))))
(define (take5 xs)
(cons (car xs) (take4 (cdr xs))))

If you're mapping a function over a list in rackect how can you get a reference to the next element?

If I have a list and I map a lambda function over it how can I get a reference to the next or previous item while processing the current one?
(map (lambda (x) x) '(1 2 3))
How would I reference the previous or next element while processing x?
John McCarthy originally made maplist and it's defined in CL still and predates map(car). It's definition in Scheme would be something like:
(define (maplist fun lst)
(if (null? lst)
'()
(cons (fun lst) (maplist fun (cdr lst)))))
(maplist values '(1 2 3 4)) ; ==> ((1 2 3 4) (2 3 4) (3 4) (4))
It's slightly more difficult to get each element like map but if you need more than the first then it's perfect.
Start with your one list, construct two other lists, one 'shifted' right, and the other 'shifted' left. Like this:
(define (process func x)
(let ((to-front (cons 'front (reverse (cdr (reverse x)))))
(to-rear (append (cdr x) (list 'rear))))
(map func to-front x to-rear)))
Note that the stuff above with reverse is because map expects all lists to have the same length. So when adding to the front, you need to remove one from the tail.
Also, the provided func needs to accept three arguments.
> (process list '(a b c))
((front a b) (a b c) (b c rear))
You can always use map on two zipped lists, i.e.
(import (srfi srfi-1)) ; or use some zip implementation
(define a '(1 2 3 4 5))
(map (lambda (x) x)
(zip a
(append (cdr a) (list (car a)))))
which results in ((1 2) (2 3) (3 4) (4 5) (5 1)).
Of course, the above assumes "periodic" boundary conditions for the lists (you should modify the boundary conditions for your case).
And also you would need to modify the lambda to handle pairs of elements.
For simplicity let's take the case of two elements at a time -- the current and next one. So if you have (list 1 2 3), and a function that takes this and next args, you want it to be called with:
1 2
2 3
3 <some value, let's say 3>
You could write that concisely as:
(map f xs (append (drop xs 1) (list (last xs))))
However the drop and append-ing means that's not the fastest way to do it. Instead you could write a map-slide-pairs function to do it more directly:
#lang racket/base
(require racket/match)
;; map a list as "sliding pairs". For example:
;; (map-slide-pairs cons '(1 2 3)) ==> '((1 . 2)
;; (2 . 3)
;; (3 . 3))
(define (map-slide-pairs f xs #:last-val [last-val #f])
;; Concise implementation:
;; (map f xs (append (drop xs 1) (list (last xs)))))
;; Faster implementation:
(let loop ([xs xs])
(match xs
[(list) (list)]
[(list this) (list (f this (or last-val this)))]
[(list this next more ...) (cons (f this next)
(loop (cons next more)))])))
(module+ test
(require rackunit)
(check-equal? (map-slide-pairs cons '(1 2 3))
'([1 . 2][2 . 3][3 . 3]))
(check-equal? (map-slide-pairs cons '(1 2 3) #:last-val 100)
'([1 . 2][2 . 3][3 . 100])))
Hopefully you can see how to extend this and make a "map-slide-triples" function that would be called with the previous, current, and next elements of the list.

Find how many times each number occurs in list

If we had a list A holding (1 2 1 1 2 3 3 4 4 4), how could we get a new list B with ((1 . 30) (2 . 20) (3 . 20) (4 . 30)) in it, such that the number_after_dot is the percentage of the number_before_dot in the list A.
For example 1 is 30% of list A, 2 is 20% of list A, etc..
(1 . 30) is a pair, which could be made by (cons 1 30)
I think what you want to do is calculate the percentage of the list that is equal to each element. You used the word "unique" but that a bit confusing since your list has no unique elements. This is based on your sample input and output, where the list (1 2 1 1 2 3 3 4 4 4) is composed of "30% ones".
You can break this down roughly into a recursive algorithm consisting of these steps:
If the input list is empty, return the empty list.
Otherwise, get the first element. Calculate how many times it occurs in the list.
Calculate the percentage, and cons the element with this percentage.
Remove all the occurrences of the first item from the cdr of the list.
Recurse on this new list, and cons up a list of (element . percentage) pairs.
To do the first part, let's use filter:
> (filter (lambda (x) (eq? (car A) x)) A)
(1 1 1)
With your list A, this will return the list (1 1 1). We can then use length to get the number of times it occurs:
> (length (filter (lambda (x) (eq? (car A) x)) A))
3
To calculate the percentage, divide by the number of elements in the whole list, or (length A) and multiply by 100:
> (* 100 (/ (length (filter (lambda (x) (eq? (car A) x)) A)) (length A)))
30
It's easy to cons this with the element (car A) to get the pair for the final list.
To do the second step, we can use remove which is the inverse of filter: it will return a list of all elements of the original list which do not satisfy the predicate function:
> (remove (lambda (x) (eq? (car A) x)) A)
(2 2 3 3 4 4 4)
This is the list we want to recurse on. Note that at each step, you need to have the original list (or the length of the original list) and this new list. So you would need to somehow make this available to the recursive procedure, either by having an extra argument, or defining an internal definition.
There might be more efficient ways I'm sure, or just other ways, but this was the solution I came up with when I read the question. Hope it helps!
(define (percentages all)
(let ((len (length all))) ; pre-calculate the length
;; this is an internal definition which is called at ***
(define (p rest)
(if (null? rest)
rest
;; equal-to is a list of all the elements equal to the first
;; ie something like (1 1 1)
(let ((equal-to (filter (lambda (x) (eq? (car rest) x))
rest))
;; not-equal-to is the rest of the list
;; ie something like (2 2 3 3 4 4 4)
(not-equal-to (remove (lambda (x) (eq? (car rest) x))
rest)))
(cons (cons (car rest) (* 100 (/ (length equal-to) len)))
;; recurse on the rest of the list
(p not-equal-to)))))
(p all))) ; ***
The question formulation is very close to the idea of run-length encoding. In terms of run-length encoding, you can use a simple strategy:
Sort.
Run-length encode.
Scale the run lengths to get percentages.
You can implement run-length encoding like this:
(define (run-length-encode lst)
(define (rle val-lst cur-val cur-cnt acc)
(if (pair? val-lst)
(let ((new-val (car val-lst)))
(if (eq? new-val cur-val)
(rle (cdr val-lst) cur-val (+ cur-cnt 1) acc)
(rle (cdr val-lst) new-val 1 (cons (cons cur-val cur-cnt) acc))))
(cons (cons cur-val cur-cnt) acc)))
(if (pair? lst)
(reverse (rle (cdr lst) (car lst) 1 '()))
'()))
and scaling looks like:
(define (scale-cdr count-list total-count)
(define (normalize pr)
(cons (car pr) (/ (* 100 (cdr pr)) total-count)))
(map normalize count-list))
Now we need something to sort a list. I'll just use the sort function in racket (adapt as needed). The function to calculate the percentages for each number in the list is then:
(define (elem-percent lst)
(scale-cdr (run-length-encode (sort lst <)) (length lst)))
Some examples of use:
> (elem-percent '())
'()
> (elem-percent (list 1 2 3 4 5))
'((1 . 20) (2 . 20) (3 . 20) (4 . 20) (5 . 20))
> (elem-percent (list 1 2 1 1))
'((1 . 75) (2 . 25))
> (elem-percent (list 1 2 1 1 2 3 3 4 4 4))
'((1 . 30) (2 . 20) (3 . 20) (4 . 30))