Scheme / Racket insert-everywhere function with sublists - functional-programming

So I've been trying to solve this problem:
Given an element E and a list L insert E into every position in the list L (so the result is a list of lists). Example:
(insert-everywhere 'a '(b c)) would give ((a b c) (b a c) (b c a))
This is easy enough but there is one other condition in my problem that is making it difficult for me - if an element of L is a list itself then the element must also be inserted into every position in the sublist. For example:
(insert-everywhere 'd '(a (b c))) would return: ((d a (b c)) (a d (b c)) (a (d b c)) (a (b d c)) (a (b c d)) (a (b c) d)).
This is the code I have so far (which I mostly lifted from here):
#lang racket
(define (insert-at pos elmt lst)
(if (empty? lst) (list elmt)
(if (list? (car lst)) (insert-everywhere elmt (car lst))
(if (= 1 pos)
(cons elmt lst)
(cons (first lst)
(insert-at (- pos 1) elmt (rest lst)))))))
(define (insert-everywhere sym lst)
(remove-duplicates
(map (lambda (i)
(insert-at i sym lst))
(range 1 (+ 2 (length lst))))))
where this line: (if (list? (car lst)) (insert-everywhere elmt (car lst)) is supposed to handle the sublists but it isn't working. (If I run (insert-everywhere 'd '(a (b c))) with the above code I get ((d a (b c)) (a (d b c) (b d c) (b c d))))
If anyone has suggestions on how to approach this differently I'd be happy to hear.

I wouldn't do indexing as it is very inefficient. Rather reverse the input list and build the list from end to beginning making the results in reverse order. You have a current list that you add elements to with cons that you use to add new additions to the results and each level each result that existed gets the one element added too.
As parameters you have state. When i made a reference I used result and cur and typically my iteration did like this for (insert-everywhere 'd '(a b c)):
lst cur results
(c b a) () ((d))
(b a) (c) ((d c) (c d))
(a) (b c) ((d b c) (b d c) (b c d))
() (a b c) ((d a b c) (a d b c) (a b d c) (a b c d)))
Now adding support for sublists are just doing the same with them and then do a map such that you create one result per sublist in the result, adding cur in addition to adding it as an element.
Notice all new results are just cur with an added inserted element and all th erest gets a new element in fron which is the first element of the input. cur will grow and it is shared so only the elements up to the inserted element will be unique to that sub result.
I have a working implementation, but it's no fun getting the solution prematurely. Have fun.

Related

How to go count all of the atoms in a list (or a list of nested lists) when you use recursion

I am creating a recursive function that counts the number of atoms inside a list. It should be able to count the atoms of lists that are nested.
For example: (a (b c) e d) or (a (b c (g e)) e d), it should count b and c separately or b, c, e, and d separately and not as a whole.
This is the function that I have created:
(defun count-atoms (mylist)
(cond
((null mylist) 0)
((listp (car mylist)) (count-atoms (car mylist)))
((atom (car mylist)) (+ 1 (count-atoms (rest mylist))))
)
)
The output I get is 3 but it should be 5 (based from (a (b c) e d)). I am guessing that the function stops the moment it reaches c. How do i make the function not stop at c and make it go back to the outermost list.
Here's a way we can reason about the problem -
If the input is null, return zero
'( )
^
| 0 atoms
(inductive) Otherwise the input has at least one element. If car is a list, call count-elements on car and cdr. Add the two results together and return.
'( a b c d ... )
^ ^
| | count atoms in cdr <-
| \
| count atoms in sublist <------\_ add together
(inductive) Otherwise the input has at least one element that is not a list. Call count-elements on cdr. Add one to the result and return.
'( a b c d ... )
^ ^
| | count atoms in cdr <-
| \
| one atom <-----------------\_ add together
Do you see where your program differs?
Your mistake is that you are ignoring the tail in the second clause.
(defun count-atoms (tree)
"Count atoms in all leaves of the tree, ignoring terminating NIL."
(if tree
(+ (if (atom (car tree))
1
(count-atoms (car tree)))
(count-atoms (cdr tree)))
0))
now
(count-atoms '(a (b c) e d))
==> 5
(count-atoms '(a (b c (g e)) e d))
==> 7
(count-atoms '(a (b c (g e)) nil e d))
==> 8

Racket, writing function that find nth element in list

In order to understand functional programing, please help me to write a function that output nth element of a list,
Allowed command:
define lambda cond else empty empty? first rest cons list
list? = equal? and or not + - * / < <= > >=
Sample output:
(fourth-element '(a b c d e)) => d
(fourth-element '(x (y z) w h j)) => h
(fourth-element '((a b) (c d) (e f) (g h) (i j))) => (list 'g 'h)
or ‘(g h)
(fourth-element '(a b c)) => empty
I could write this in python, but I am not family with racket syntax,
def element(lst, x=0):
counter = x;
if (counter >= 3):
return lst[0]
else:
return element(lst[1:],x+1)
a = [1,2,3,4,5,6]
print(element(a))
The Output is 4
Comparing with code above in python. What is equivalent behavior in function that create local variable counter. What is "keyword" for return
It looks like you came up with an answer of your own. Nice work! I would recommend a more generic nth procedure that takes a counter as an argument. This allows you to get any element in the input list
(define (nth lst counter)
(cond ((null? lst) (error 'nth "index out of bounds"))
((= counter 0) (first lst))
(else (nth (rest lst) (- counter 1)))))
Now if you want a procedure that only returns the 4th element, we create a new procedure which specializes the generic nth
(define (fourth-element lst)
(nth lst 3))
That's it. Now we test them out with your inputs
(define a `(1 2 3 (4 5) 7))
(define b `(1 2 3))
(define c `((a b)(c d)(e f)(g h)(i j)))
(define d `(a b c))
(fourth-element a) ; '(4 5)
(fourth-element b) ; nth: index out of bounds
(fourth-element c) ; '(g h)
(fourth-element d) ; nth: index out of bounds
Note, when the counter goes out of bounds, I chose to raise an error instead of returning a value ("empty") like your program does. Returning a value makes it impossible to know whether you actually found a value in the list, or if the default was returned. In the example below, notice how your procedure cannot differentiate the two inputs
(define d `(a b c))
(define e `(a b c ,"empty"))
; your implementation
(fourth-element e) ; "empty"
(fourth-element d) ; "empty"
; my implementation
(fourth-element e) ; "empty"
(fourth-element d) ; error: nth: index out of bounds
If you don't want to throw an error, there's another way we can encode nth. Instead of returning nth element, we can return the nth pair whose head contains the element in question.
Below, nth always returns a list. If the list is empty, no element was found. Otherwise, the nth element is the first element in the result.
(define (nth lst counter)
(cond ((null? lst) '())
((= counter 0) lst)
(else (nth (rest lst) (- counter 1)))))
(define (fourth-element lst)
(nth lst 3))
(define a `(1 2 3 (4 5) 7))
(define b `(1 2 3))
(define c `((a b)(c d)(e f)(g h)(i j)))
(define d `(a b c))
(define e `(a b c ,"empty"))
(fourth-element a) ; '((4 5) 7)
(fourth-element b) ; '()
(fourth-element c) ; '((g h) (i j))
(fourth-element d) ; '()
(fourth-element e) ; '("empty")
Hopefully this gets you to start thinking about domain (procedure input type) and codomain (procedure output type).
In general, you want to design procedures that have natural descriptions like:
" nth takes a list and a number and always returns a list" (best)
" nth takes a list and a number and returns an element of the list or raises an exception if the element is not found" (good, but now you must handle errors)
Avoid procedures like
" nth takes a list and a number and returns an element of the list or a string literal "empty" if the element is not found" (unclear codomain)
By thinking about your procedure's domain and codomain, you have awareness of how your function will work as it's inserted in various parts of your program. Using many procedures with poorly-defined domains lead to disastrous spaghetti code. Conversely, well-defined procedures can be assembled like building blocks with little (or no) glue code necessary.
Here is how to write it in Python:
def nth(lst, idx=0):
if (len(lst) == 0):
return "empty"
elif (idx == 0):
return lst[0]
else:
return nth(lst[1:], idx - 1)
nth([1,2,3], 1)
# ==> 2
def fourth-element(lst):
return nth(lst, 4)
Same in Scheme/Racket:
(define (nth lst idx)
(cond ((empty? lst) empty) ; more effiecent than (= (length lst) 0)
((= idx 0) (first lst))
(else (nth (rest lst) (- idx 1))))
(nth '(1 2 3) 1)
; ==> 2
(define (fourth-element lst)
(nth lst 4))
There is no keyword for return. Every form returns the last evaluated code:
(if (< 4 x)
(bar x)
(begin
(display "print this")
(foo x)))
This if returns either the result of (bar x) or it prints "print this" then returns the result of (foo x). The reason is that for the two outcomes of the if they are the tail expressions.
(define (test x)
(+ x 5)
(- x 3))
This function has two expressions. The first is dead code since it has no side effect and since it's not a tail expression, but the (- x 3) is what this function returns.
(define (test x y)
(define xs (square x))
(define ys (square y))
(sqrt (+ xs ys)))
This has 3 expressions. The first two has side effects that it binds two local variables while the third uses this to compute the returned value.
(define a `(1 2 3 (4 5) 7))
(define b `(1 2 3))
(define c `((a b)(c d)(e f)(g h)(i j)))
(define d `(a b c))
(define (my-lst-ref lst counter)
(cond[(>= counter 3) (first lst)]
[else (my-lst-ref (rest lst)(+ counter 1))]
)
)
(define (fourth-element lst)
(cond[(>= (list-length lst) 4) (my-lst-ref lst 0)]
[else "empty"]))
(fourth-element a)
(fourth-element c)
(fourth-element d)
Output:
(list 4 5)
(list 'g 'h)
"empty"

Group the elements of a set into disjoint subsets using iteration instead of recursion

I came across Pascal Bourguignon's solutions of the 99 Lisp problems and was wondering if his recursive solution of problem 27 using a nested mapcan-mapcar-construct could also be written using nested loops.
His solution is definitely very elegant:
(defun group (set sizes)
(cond
((endp sizes)
(error "Not enough sizes given."))
((endp (rest sizes))
(if (= (first sizes) (length set))
(list (list set))
(error "Cardinal mismatch |set| = ~A ; required ~A"
(length set) (first sizes))))
(t
(mapcan (lambda (combi)
(mapcar (lambda (group) (cons combi group))
(group (set-difference set combi) (rest sizes))))
(combinations (first sizes) set)))))
The function combinations is defined here as:
(defun combinations (count list)
(cond
((zerop count) '(())) ; one combination of zero element.
((endp list) '()) ; no combination from no element.
(t (nconc (mapcar (let ((item (first list)))
(lambda (combi) (cons item combi)))
(combinations (1- count) (rest list)))
(combinations count (rest list))))))
I started with a simple approach:
(defun group-iter (set sizes)
(loop :with size = (first sizes)
:for subgroup :in (combination size set)
:for remaining = (set-difference set subgroup)
:collect (list subgroup remaining) :into result
:finally (return result)))
which results in:
> (group-iter '(a b c d e f) '(2 2 2))
(((A B) (F E D C)) ((A C) (F E D B)) ((A D) (F E C B)) ((A E) (F D C B))
((A F) (E D C B)) ((B C) (F E D A)) ((B D) (F E C A)) ((B E) (F D C A))
((B F) (E D C A)) ((C D) (F E B A)) ((C E) (F D B A)) ((C F) (E D B A))
((D E) (F C B A)) ((D F) (E C B A)) ((E F) (D C B A)))
But now I am totally failing to implement the nesting which takes care of the further processing of remaining. As far as I understood there is always a way to express a recursion with a iteration but how does it look like here?

Number of same atomic elements in a list like (a (a b) (b c))

I would like to ask you for help with the following:
When I apply a procedure number-of-elements on the list, I need to get a list of pairs, where on the first place in the pair is the element and on the second place (after the dot) there is a number of elements occurred in the list.
For example, when typing this:
(number-of-elements '((a b c) a (b c) c (a b b)))
I got this:
((a . 3) (b . 4) (c . 3))
So far I have a code working on regular list (a b a d).
(define number-of-elements
(lambda (lst)
(define exclude
(lambda (sznm key)
(foldr (lambda (ass result)
(if (equal? (car ass) key)
result
(cons ass result)))
'()
sznm)))
(foldr (lambda (key bag)
(cond ((assoc key bag)
=> (lambda (old)
(let ((new (cons key (+ (cdr old) 1))))
(cons new (exclude bag key)))))
(else (let ((new (cons key 1)))
(cons new bag)))))
'()
lst)))
But if I use it on:
(number-of-elements '((a b c) a (b c) c (a b b)))
I got this:
(((a b c) . 1) (a . 1) ((b c) . 1) (c . 1) ((a b b) . 1))
I know I need to use a deep recursion, but I do not know, how to implement it into the code I actually have.
You already did most of the work counting the elements - but see the different implementations of bagify for a simpler implementation. One straightforward solution for dealing with nested sublists would be to flatten the input list before counting the elements:
(number-of-elements
(flatten
'((a b c) a (b c) c (a b b))))
=> '((a . 3) (b . 4) (c . 3))
If your interpreter doesn't define flatten, it's easy to implement:
(define (flatten lst)
(if (not (list? lst))
(list lst)
(apply append (map flatten lst))))
This is the idiomatic way to think about solutions in Scheme: decompose the problem in parts, then use built-in procedures to solve each subpart, and finally combine them.

Scheme: Counting types using recursion

Structure Definition:
(define-struct movie (title genre stars))
;; title is a nonempty string
;; genre is a nonempty string
;; stars us a list of nonempty strings
I am trying to write a scheme function that consumes a list of movies and produces the genre that occurs most often.
So far, I have the following:
(define (popular-gnere movies)
(local
[(define acc movies genre)
(cond
[(empty? movies) genre]
[(equal? genre (movie-genre (first movies)))
(acc (rest movies genre)))
I'm stuck as to how I can keep count of how many times a specific genre has appeared in a given list of movies.
I understand that accumulated recursion in this case would be most efficient but am having trouble completing my accumulator.
Why don't you fix your parentheses problem and indent the code properly. Press CRTL+i. Where the identation is wrong you probably have missing parentheses. Press Run to evaluate and you'd get proper error messages. When you have something that doesn't produce errors, update this question.
The answer your question you add more parameters to your local procedures than the global. That way you hae a parameter that can hold a count that you increase when you find the search element in the current element.eg.
(define (length lst)
(define (length-aux lst cnt)
(if (null? lst)
cnt
(length-aux (cdr lst) (add1 cnt))))
(length-aux lst 0))
Or better with named let
(define (length lst)
(let length-aux ((lst lst) (cnt 0))
(if (null? lst)
cnt
(length-aux (cdr lst) (add1 cnt)))))
EDIT
I recommend having at least 4 helper procedures that takes each their part of a problem. (Less if you make use racket's own remove, count, and argmax). Note that there are probably many other ways to solve this but this is how I would have solved it without a hash table.
Since you are only interested in genre the first thing to imagine is that you can do (map movie-genre lst) so that you get a list of genres to work with in your main helper.
In your main helper you can build up a list of cons having genre and count. To do that you use a helper count that (count 'c '(a b c d c c a) 0) ==> 3 and you just take the first genre and count the list for those as the first accumulated value, then process the result of (remove 'c '(a b c d c c a) '()) ==> (a d b a) on the rest of the list.
When processing is done you have in your accumulator ((a . 4) (b . 6) ...) and you need a helper (max-genre 'a 4 '((b . 6) (c . 20) (d . 10))) ; ==> c
The main helper would look something like this:
(define (aux lst acc)
(if (null? lst)
(max-genre (caar acc) (cdar acc) (cdr acc))
(aux (remove (car lst) lst '())
(cons (cons (car lst) (count (car lst) lst 0)) acc))))
Now you could do it a lot simpler with a hash table in one pass. You'd still have to have max-genre/argmax after reading all elements once.
First you need to settle on a key-value datatype. You could use association lists, but hash tables are a more efficient choice.
Let's start with a short list:
(define-struct movie (title genre stars))
(define films
(list
(make-movie "Godfater" "Crime" '("Marlon Brando" "Al Pacino"))
(make-movie "Rambo" "Thriller" '("Sylvester Stallone"))
(make-movie "Silence of the Lambs" "Crime" '("Jodie Foster" "Anthony Hopkins"))))
and create an empty hash table
(define h (make-hash))
Now we process every film, updating the hash table as we go:
> (for-each (lambda (e) (hash-update! h e add1 0)) (map movie-genre films))
> h
'#hash(("Thriller" . 1) ("Crime" . 2))
Now we need to find the highest count:
> (hash-values h)
'(1 2)
> (define most (foldl (lambda (e r) (if (> e r) e r)) 0 (hash-values h)))
> most
2
So 2 is our highest count. Now we create a list of all genres with count 2:
> (hash->list h)
'(("Thriller" . 1) ("Crime" . 2))
> (foldl
(lambda (e r) (if (= (cdr e) most) (cons (car e) r) r))
null
(hash->list h))
'("Crime")
Putting it all together:
(define (count-by-genre lst)
(define h (make-hash))
(for-each (lambda (e) (hash-update! h e add1 0)) (map movie-genre lst))
(define most (foldl (lambda (e r) (if (> e r) e r)) 0 (hash-values h)))
(foldl
(lambda (e r) (if (= (cdr e) most) (cons (car e) r) r))
null
(hash->list h)))
But this is quite inefficient, for several reasons:
after updating the hash table, we have to re-iterate over it, create a list and then apply foldl just to find the highest value, whereas we could have just kept note of it while updating the hash table
then again we create a full list (hash->list) and a final result list using foldl.
Lots of consing and stuff. An alternative, more efficient version using Racket-specific for constructs, could be:
(define (count-by-genre lst)
(define h (make-hash))
(define most
(for/fold ((highest 0)) ((e (in-list (map movie-genre lst))))
(define new (add1 (hash-ref h e 0)))
(hash-set! h e new)
(max highest new)))
(for/fold ((res null)) (((k v) (in-hash h)))
(if (= v most) (cons k res) res)))

Resources