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))
Related
We need a Scheme function called nondecreaselist, which takes in a list of numbers and outputs a list of lists, which overall has the same numbers in the same order, but grouped into lists that are non-decreasing.
For example, if we have input (1 2 3 4 1 2 3 4 1 1 1 2 1 1 0 4 3 2 1), the output should be:
((1 2 3 4) (1 2 3 4) (1 1 1 2) (1 1) (0 4) (3) (2) (1))
How would you implement this? I know we have to use recursion.
My attempt so far:
(define (nondecreaselist s)
(cond ((null? s) '())
((cons (cons (car s)
((if (and (not (null? (cadr s)))
(not (> (car s) (cadr s))))
((cadr s))
('()))))
(nondecreaselist (cdr s))))))
However, this gives me the error:
(int) is not callable:
(define decrease-list
(lambda (l)
((lambda (s) (s s l cons))
(lambda (s l col)
;; limitcase1: ()
(if (null? l)
(col '() '())
;; limitcase2: (a1)
(if (null? (cdr l))
(col l '())
(let ((a1 (car l)) (a2 (cadr l)))
;; limitcase3: (a1 a2)
(if (null? (cddr l))
(if (>= a2 a1)
(col l '())
(col (list a1) (list (cdr l))))
;; most usual case: (a1 a2 ...)
(s s (cdr l)
(lambda (g l*)
(if (>= a2 a1)
(col (cons a1 g) l*)
(col (list a1) (cons g l*)))))))))))))
1 ]=> (decrease-list '(1 2 3 4 1 2 3 4 1 1 1 2 1 1 0 4 3 2 1))
;Value: ((1 2 3 4) (1 2 3 4) (1 1 1 2) (1 1) (0 4) (3) (2) (1))
I did not comment it, if you have questions you can ask but I think you can also study yourself the code I wrote for you now.
Note also that one can consider the limit cases () and (a1) out of the loop and check these cases only once:
(define decrease-list
(lambda (l)
;; limitcase1: ()
(if (null? l)
'()
;; limitcase2: (a1)
(if (null? (cdr l))
(list l)
((lambda (s) (s s l cons))
(lambda (s l col)
(let ((a1 (car l)) (a2 (cadr l)))
;; limitcase3: (a1 a2)
(if (null? (cddr l))
(if (>= a2 a1)
(col l '())
(col (list a1) (list (cdr l))))
;; most usual case: (a1 a2 ...)
(s s (cdr l)
(lambda (g l*)
(if (>= a2 a1)
(col (cons a1 g) l*)
(col (list a1) (cons g l*)))))))))))))
There are a few problems with the posted code. There is no test expression in the second cond clause; there are too many parentheses around the if and its clauses. Perhaps the most significant problem is that the code is attempting to build a non-decreasing list, which is to be consed to the result of (nondecreaselist (cdr s)), but when the non-decreasing sequence is more than one number long this starts again too soon in the input list by going all the way back to (cdr s).
Fixing Up OP Code
The logic can be cleaned up. OP code already is returning an empty list when input is an empty list. Instead of testing (null? (cadr s)) (when (cdr s) is '(), cadr won't work on s), one could test (null? (cdr s)) before code attempts a (cadr s). But it is even better to move this logic; when the input list contains one element, just return a list containing the input list: ((null? (cdr s)) (list s)).
Instead of (and (not (> ;... the logic can be made more clear by testing for > and executing the appropriate action. In this case, when (> (car s) (cadr s)) a new sublist should be started, and consed onto the list of sublists that is the result returned from nondecreaselist.
Otherwise, (car s) should be added to the first sublist in the result returned from nondecreaselist. To accomplish this, we need to construct the return list by consing s onto the first sublist, and then consing that new sublist back onto the cdr of the list of sublists that is the result returned from nondecreaselist.
Here is some revised code:
(define (nondecreaselist s)
(cond ((null? s) '())
((null? (cdr s)) (list s))
((> (car s) (cadr s))
(cons (list (car s))
(nondecreaselist (cdr s))))
(else
(let ((next (nondecreaselist (cdr s))))
(cons (cons (car s)
(car next))
(cdr next))))))
Using a Helper Function
Another approach would be to define a helper function that takes an input list and an accumulation list as arguments, returning a list of lists. The helper function would take numbers from the front of the input list and either add them to the accumulator, creating a non-decreasing list, or it would cons the accumulated non-decreasing list to the result from operating on the rest of the input.
If the input lst to the helper function ndl-helper is empty, then a list containing the accumulated non-decreasing list sublst should be returned. Note that sublst will need to be reversed before it is returned because of the way it is constructed, as described below.
If the accumulator sublst is empty, or if the next number in the input list is greater-than-or-equal-to the largest number in the sublst, then the next number should simply be added to the sublst. By consing the number onto the front of sublst, only the car of sublst needs to be checked, since this will always be the largest (or equal to the largest) value in sublst. But, since sublst is in reverse order, it will need to be reversed before adding it to the growing list of lists.
Otherwise, lst is not empty, and sublst is not empty, and the next number in the input list is less than the largest number in sublst. Thus, a new sublist needs to be started, so the old sublst is reversed and consed onto the result of the remaining computation done by calling the helper function on the remaining lst with an empty accumulator sublst:
(define (nondecreaselist-2 lst)
(define (ndl-helper lst sublst)
(cond ((null? lst) (list (reverse sublst)))
((or (null? sublst)
(>= (car lst) (car sublst)))
(ndl-helper (cdr lst) (cons (car lst) sublst)))
(else
(cons (reverse sublst) (ndl-helper lst '())))))
(ndl-helper lst '()))
Both functions work:
> (nondecreaselist '(1 2 3 4 1 2 3 4 1 1 1 2 1 1 0 4 3 2 1))
((1 2 3 4) (1 2 3 4) (1 1 1 2) (1 1) (0 4) (3) (2) (1))
> (nondecreaselist-2 '(1 2 3 4 1 2 3 4 1 1 1 2 1 1 0 4 3 2 1))
((1 2 3 4) (1 2 3 4) (1 1 1 2) (1 1) (0 4) (3) (2) (1))
****What I tried****
(define(help num)
(if(= num 1)
num
(cons(num (help( - num 1))))))
;i called this defination in the bottom one
(define (list-expand L)
(cond
[(empty? L)'()]
[(=(car L)1)(cons(car L)(list-expand (cdr L)))]
[(>(car L)1) (cons(help(car L)(list-expand(cdr L))))]))
In the help procedure, the base case is incorrect - if the output is a list then you must return a list. And in the recursive step, num is not a procedure, so it must not be surrounded by brackets:
(define (help num)
(if (<= num 0)
'()
(cons num (help (- num 1)))))
And in list-expand, both recursive steps are incorrect. You just need to test whether the list is empty or not, calling help with the correct number of parameters; use append to combine the results, because we're concatenating sublists together:
(define (list-expand L)
(if (empty? L)
'()
(append (help (car L)) (list-expand (cdr L)))))
That should work as expected, but please spend some time studying Scheme's syntax, you still have trouble with the basics, for instance, when and where to use brackets...
(list-expand '(3 2))
=> '(3 2 1 2 1)
Just for fun - a non-recursive solution in Racket:
(append-map (lambda (n) (stream->list (in-range n 0 -1))) '(3 2))
;; or:
(append-map (lambda (n) (for/list ((x (in-range n 0 -1))) x)) '(3 2))
Returning:
'(3 2 1 2 1)
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)
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.
I'm trying to write a function that works like remove-duplicates, but it instead takes two lists as input, the first specifying characters for which duplication is not allowed, and the second being a list of various atoms which is to be pruned.
Currently I have this:
(defun like-remove-duplicates (lst1 lst2)
(if(member (first lst1) lst2)
(remove-if #'(lambda (a b)
(equals a b))lst1 lst2)))
I know it's not anywhere near right, but I can't figure out what I need to do to perform this function. I know I essentially need to check if the first item in list1 is in list2, and if so, remove its duplicates (but leave one) and then move onto the next item in the first list. I envisioned recursion, but it didn't turn out well. I've tried researching, but to no avail.
Any help?
CL-USER> (defun remove-duplicates-from-list (forbidden-list list)
(reduce (lambda (x y)
(let ((start (position y x)))
(if start
(remove y x :start (1+ start))
x)))
forbidden-list
:initial-value list))
REMOVE-DUPLICATES-FROM-LIST
CL-USER> (remove-duplicates-from-list '(1 2) '(1 2 1 3))
(1 2 3)
CL-USER> (remove-duplicates-from-list '(1 2) '(1 2 1 3 2))
(1 2 3)
CL-USER> (remove-duplicates-from-list '(1 2) '(1 2 1 3 2 4))
(1 2 3 4)
CL-USER> (remove-duplicates-from-list '(2 1) '(1 2 1 3 2 4))
(1 2 3 4)
CL-USER> (remove-duplicates-from-list '(2 1) '(0 1 2 1 3 2 4))
(0 1 2 3 4)
CL-USER> (remove-duplicates-from-list '(2 1) '(0 2 3 2 4))
(0 2 3 4)
CL-USER> (remove-duplicates-from-list '(2 1) '(0 2 2 3 4))
(0 2 3 4)
Recursion is performed by reduce (because here we have the most common recursion pattern: feed the result of previous iteration to the next) and removeing is done with the help of :start parameter, that is the offset after the first encounter (found by position) of the value being removed currently.
It's also important to account the case, when the value isn't found and position returns nil.
Something like this should work and have acceptable time-complexity (at the cost of soem space-complexity).
(defun like-remove-duplicates (only-once list)
"Remove all bar the first occurence of the elements in only-once from list."
(let ((only-once-table (make-hash-table))
(seen (make-hash-table)))
(loop for element in only-once
do (setf (gethash element only-once-table) t))
(loop for element in list
append (if (gethash element only-once-table)
(unless (gethash element seen)
(setf (gethash element seen) t)
(list element))
(list element)))))
This uses two state tables, both bounded by the size of the list of elements to include only once and should be roughly linear in the sum of the length of the two lists.
(defun remove-listed-dups (a b)
(reduce (lambda (x y) (if (and (find y a) (find y x)) x (cons y x)))
b :initial-value ()))