Expression inside a list in Racket - list

If I have a list like this (define lst '((,(car '(1 2)) (1 2)) (,(car '(3 4)) (3 4))) ) and I try to 'evaluate' the expression (i.e. take as result '((1 (1 2)) (3 (3 4)))) I obtain the same list that I have.
I know that if I use (quoasiquote ((,(car '(1 2)) (1 2)) (,(car '(3 4)) (3 4))) I obtain what I'm looking for, but the problem is in an execution with an iterative method, where (I think) I'm not capable of take only the values, without being a list. (i.e. take only the second part of the quoasiquote expression).
For example, if I use for/list and I do (list-ref lst 0), I obtain '(,(car '(1 2)) (1 2)), when I want (,(car '(1 2)) (1 2)) for use it in quoasiquote function.
How can I obtain the expression inside a list and evaluate it?
Thank you.

So when you quote data it's like single quotes in some popular Algol dialects like perl. eg print '$_'; actually prints $_ and not the value the variable $_ represents. If you would have used double quotes the variables in the string is expanded to it's value.
In Scheme we have (quote x) which is 'x. Nothing in it will ever going to become evaluated since it's quoted with '. If it were `x, which is the same as (quasiquote x) then Scheme looks for ,expression (same as (unquote expression) ) and ,#expression (same as (unquote-splicing expression) ). These quote forms are NOT procedures but macros. It transforms your static code to different static code.
So (begin (define test 10) `((1 2) (3 ,(+ test 4)))) ; ==> ((1 2) (3 14))
What Scheme really does is transform it into (list '(1 2) (list 3 (+ test 4))), while if it was `((3 ,(+ test 4)) (1 2)) it turns into (cons (list 3 (+ test 4)) '((1 2))) since the tail can be a constant but the head cannot if it needs to have a evaluated tail.
I get the impression from you question that you think you can store the text and expand them later like (let ((x '((1 2) (3 ,(+ test 4))))) `x) but that will not work since quasiquote is a macro and thus it will evaluate to x. and (let ((x '((1 2) (3 ,(+ test 4))))) `,x) would evaluate to ((1 2) (3 ,(+ test 4))).

I'm pretty confident that we have an XY-problem here because what you describe is pretty unusual. Also, the only solution I imagine here is using eval, which again shows that you're probably heading into the wrong direction.
But here's a little attempt at what I believe you want:
(define lst '((list (car '(1 2)) '(1 2))
(list (car '(3 4)) '(3 4))))
(define-namespace-anchor nsa)
(define ns (namespace-anchor->namespace nsa))
(for/list ((i lst))
(define e (eval i ns))
(printf "evaluating ~a to ~a\n" i e)
e)
which will print
evaluating (list (car '(1 2)) '(1 2)) to (1 (1 2))
evaluating (list (car '(3 4)) '(3 4)) to (3 (3 4))
and evaluate to
'((1 (1 2)) (3 (3 4)))

Related

Functions to print and replace elements in a list

I am trying to implement two functions : subterm and replace.
subterm takes two lists as arguments and prints the element in the first list that is reached after exhausting the second list.
For example, calling
(subterm '(1 2 (3 4 5) (6 (7 (8) 9 10))) '(4 2 2 1))
should return
8
I have come up with the following function which prints the nth element in the list :
(define (subterm list n)
(cond
((null? list) '())
((= n 1) (car list))
(else (subterm (cdr list) (- n 1)))))
replace takes 3 lists and returns the result of replacing the reached value with the rest of the list unchanged.
for example calling :
(replace '(1 2 (3 4 5) (6 (7 (8) 9 10))) '(11 12) '(4 2 2 1))
should return :
'(1 2 (3 4 5) (6 (7 ((11 12)) 9 10)))
Again, I came up with this code which replaces the nth element in the first list with the second list, leaving the rest of the first list unchanged :
#lang racket
(define (replace list elem n)
(cond
((empty? list) empty)
((eq? n 1) (cons elem (cdr list)))
(#t (cons (car list) (replace (cdr list) elem (- n 1))))))
How do I modify these functions to take in two lists?
Edit 1:
Some examples:
> (subterm '(1 2 3 4 5) '(3))
3
> (subterm '(1 2 3 4 5) '(2))
2
> (subterm '(1 2 (3 4 5) 6 7) '(3 2))
4
Consider this example:
> (subterm '(1 2 (3 4 5) (6 (7 (8) 9 10))) '(4 2 2 1))
8
In the above example, subterm takes 2 lists. Then it reads the second list. The second list basically tells subterm to return the 1st element (8) of the 2nd element ((8)) of the 2nd element (7 (8) 9 10) of the 4th element (6 (7 (8) 9 10) of the first list (1 2 (3 4 5) (6 (7 (8) 9 10))).
> (subterm '1 '())
1
> (subterm '(1 2 (3 4 5) (6 (7 (8) 9 10))) '())
'(1 2 (3 4 5) (6 (7 (8) 9 10)))
> (replace '(1 2 3 4 5) '(6 7 8) '(3))
'(1 2 (6 7 8) 4 5)
> (replace '(1 2 3 4 5) '(6 7 8) '(2))
'(1 (6 7 8) 3 4 5)
Consider this example:
> (replace '(1 2 (3 4 5) 6 7) '(8 9) '(3 2))
'(1 2 (3 (8 9) 5) 6 7)
replace takes in three lists: first list is the list in which elements have to be replaced. The second list contains the new elements which have to be put into the first list. The third list contains the positions where the elements have to be replaced.
So, it basically replaced the 2nd element (4) of the 3rd element (3 4 5) of the first list (1 2 (3 4 5) 6 7).
> (replace '(1 2 (3 4 5) (6 (7 (8) 9 10))) '(11 12) '(4 2 2 1))
'(1 2 (3 4 5) (6 (7 ((11 12)) 9 10)))
> (replace '(1 2 (3 4 5) (6 (7 (8) 9 10))) 1000 '(4 2 2 1))
'(1 2 (3 4 5) (6 (7 (1000) 9 10)))
> (replace '(1 2 (3 4 5) (6 (7 (8) 9 10))) 'x '())
'x
> (replace '1 '(2 3 4) '())
'(2 3 4)
First of all, you're using the name subterm for two different functions. Let's call the version you provided a code example for list-ref, and make the (car list) case happen when n = 0 instead of 1:
(define (list-ref list n)
(cond
((null? list) '())
((= n 0) (car list))
(else (list-ref (cdr list) (- n 1)))))
As it turns out, list-ref is already in the racket library, so you shouldn't really have to implement it in the first place. So using that, your subterm is trivial:
(define (subterm main-list path)
(match path
('() #f)
((list n) (list-ref main-list (sub1 n)))
((cons n rest) (subterm (list-ref main-list (sub1 n)) rest))))
I tried to code the replace procedure. With my knowledge I can say this is a hard one. However I managed to make it work at least with the example you gave. You could read it, try to understand it and then try to modify it to work with any other list. I believe you'll need an extra function to make it work properly.
#lang racket
(require racket/trace)
(define (replace list elem n)
(cond
((empty? list) empty)
((eq? n 1) (cons elem (cdr list)))
(#t (cons (car list) (replace (cdr list) elem (- n 1))))))
(define replace-with-lists
(λ (items replacement path res aux)
(letrec ([splits (list-split-at items (car path) '())])
(cond
((empty? (cdr path))
; (append
; (car (list-ref res 0))
; (list (append
; (car (list-ref res 1))
; (list (append (car aux)
; (replace (list-ref aux 1) replacement (car path))
; (list-ref aux 2)))))))
(let ([result (replace splits replacement 2)])
(replace aux
(append (car result)
(list (cadr result))
(caddr result)
)
2)))
(else
(replace-with-lists
(list-ref splits 1)
replacement
(cdr path)
(foldr cons (list (list
(list-ref splits 0)
(list-ref splits 2)))
res)
splits
)))
))
)
(define list-split-at
(λ (lst place res)
(cond
((empty? lst) res)
((= 1 place) (foldl cons
(list (cdr lst))
(foldr cons (list res) (list (car lst)))
))
(else
(list-split-at (cdr lst) (- place 1) (foldr cons (list (car lst)) res))
)
)))
(trace replace-with-lists)
Ok, I am in your programming languages class, and I am aware that this assignment is due tomorrow, so I don't want to help too much, or give you the answer. I will do my best to give you some hints in case you are still struggling. The following hints are for the replace function.
First, you need a base case. We are given this with the following
(replace '(1 2 (3 4 5) (6 (7 (8) 9 10))) 'x '())
'x
(replace '1 '(2 3 4) '())
'(2 3 4)
To do this, we just need a conditional statement that checks for an empty list. It is clear that if the last argument is an empty list, we need to "return" the second to last argument. (in your code this would be "elem" and "n")
Now comes the difficult part. It is really quite simply once you realize how many built in functions scheme/racket has. Here are the only ones I used, but they made solving the problem much much easier.
(append)
(list)
(take)
(drop)
(list-ref) //this one is more of a convenience than anything.
After the turn in date has passed, I will post my solution. Hope this helped.
EDIT: As this assignment was due a few minutes, I will post my solution as I don't think that would be considered cheating.
lang racket
(define (subterm term1 lat)
(cond
[(eqv? lat '()) term1]
[(eqv? (car lat)1) (subterm (car term1) (cdr lat))]
[else (subterm (cdr term1) (cons(-(car lat)1)(cdr lat)))])
)
(define (replace term1 term2 lat)
(cond
[(eqv? lat '()) term2]
[else (append(take term1 (-(car lat)1)) (list(replace (list-ref term1 (-(car lat)1)) term2 (cdr lat))) (drop term1 (car lat)))]))
​
Those are both functions.

In clojure, why doesn't "some" function work consistently on collections?

For below, why does the last one return a nil? Function "some" doesn't work on list of lists?
(some #(= % 1) '(1 3) ) ; ==> true
(some #(= % '(1 3)) ['(1 3) '(1 2 3)] ) ; ==> true
(some #(= % '(1 3)) '('(1 3) '(1 2 3)) ) ;==> nil
You should modify the expression like this:
(some #(= % '(1 3)) '((1 3) (1 2 3)) )
=> true
You already quoted the list by using ', you don't need to quote again in the quoted list.
You can easily check what happened in REPL:
user=> '((1 3) (1 2 3))
((1 3) (1 2 3))
user=> '('(1 3) '(1 2 3))
((quote (1 3)) (quote (1 2 3)))
#Kevin
I see #ntalbs answered but I am in the habit of testing various timings. You may be curious to note the time difference I observed:
(time (some #{'(1 3)} '((1 3) (1 2 3)))) ;0.073
(time (some #(= % '(1 3)) '((1 3) (1 2 3)))) ;0.632
(time (nil? (some #{'(1 3)} '((1 3) (1 2 3))))) ;0.068
(time (nil? (some #(= % '(1 3)) '((1 3) (1 2 3))))) ;0.628
If you are processing large amounts of data this may be a useful knowledge
As ntalbs points out, the issue here is double quoting. It may be a better idea to use vectors instead of lists, or build lists with list. Both would save you some confusion and vectors have different performance characteristics (near constant random access time).
(some #(= % '(1 3)) [[1 3] [1 2 3]])
(some #(= % '(1 3)) (list (list 1 3) (list 1 2 3)))

Update the second element from a list where the first element is in another list

Maybe my title is a little bit messy..
I have a list in this form
(define a '( (1 3) (2 2) (3 3) (4 5) (5 1)))
and I want to decrement by 1, the second element for every pair which has the the first element in the given list...
Eg..
(updateA ( 1 3 4))
will result
( (1 2) (2 2) (3 2) (4 4) (5 1)
(define (updateA lst)
(for ((x lst))
(for ((y a))
(equal? x (car y)))
;;do something here
))
Here's one implementation:
(define (decrement-alist-values alist keys)
(map (lambda (ass)
(if (member (car ass) keys)
(list (car ass) (- (cadr ass) 1))
ass))
alist))
Example:
> (decrement-alist-values '((1 3) (2 2) (3 3) (4 5) (5 1))
'(1 3 4))
((1 2) (2 2) (3 2) (4 4) (5 1))
Joshua Taylor mentioned that my version technically didn't update the list via mutation. That is a fair point, so here is a mutating version:
(define (decrement-alist-values! alist keys)
(for-each (lambda (ass)
(when (member (car ass) keys)
(set-car! (cdr ass) (- (cadr ass) 1))))
alist))
Example:
> (define a `(,(list 1 3) ,(list 2 2) ,(list 3 3) ,(list 4 5) ,(list 5 1)))
> (decrement-alist-values! a '(1 3 4))
> a
((1 2) (2 2) (3 2) (4 4) (5 1))

Append a list to a sequence of lists

I have a sequence of lists:
(def s '((1 2) (3 4) (5 6)))
And I want to append another list to the tail of this sequence, i.e.
(concat-list s '(7 8))
=> '((1 2) (3 4) (5 6) (7 8))
Various approaches that (obviously) don't work:
(cons '((1 2)) '(3 4))
=> (((1 2)) 3 4)
(conj '(3 4) '((1 2)))
=> (((1 2)) 3 4)
(concat '((1 2)) '(3 4))
=> ((1 2) 3 4)
;; close, but wrong order...
(conj '((1 2)) '(3 4))
=> ((3 4) (1 2))
;; Note: vectors work - do I really have to convert entire
;; structure from lists to vectors and back again?
(conj [[1 2]] [3 4])
=> [[1 2] [3 4]]
What are some possible implementations of concat-list, or does there exist library function that does this?
If you find this collection is usually growing to the right, then you should start it off as a vector and keep it that way.That will be the most efficient and convenient.
However, if this collection mostly grows to the left and only rarely to the right, concat is probably your best option:
(concat '((1 2)) ['(3 4)])
Note that concat returns a lazy sequence, not a persistent list.
If the collection is large and grows frequently on both ends, you may want a more advanced collection type like a finger tree or a flexvec.
There may be a better solution, but here's one way:
user=> s
((1 2) (3 4) (5 6))
user=> s2
(7 8)
user=> (concat s (cons s2 '()))
((1 2) (3 4) (5 6) (7 8))

Building a 2D List

Looking for a function that would do something akin to the following:
(foo 3 2) => '( ( (1 1) (1 2) (1 3) )
( (2 1) (2 2) (2 3) ) )
Would there be any built-in function in DrRacket that accomplishes that?
The main tool that you want to use to get such things in Racket is the various for loops. Assuming that you want to create a list-based matrix structure, then this is one way to get it:
#lang racket
(define (foo x y)
(for/list ([i y])
(for/list ([j x])
(list (add1 i) (add1 j)))))
And since people raised the more general question of how to make foo create a matrix of any dimension, here's a generalized version that works with any number of arguments, and still returns the same result when called as (foo 3 2):
#lang racket
(define (foo . xs)
(let loop ([xs (reverse xs)] [r '()])
(if (null? xs)
(reverse r)
(for/list ([i (car xs)])
(loop (cdr xs) (cons (add1 i) r))))))
(Note BTW that in both cases I went with a simple 0-based iteration, and used add1 to get the numbers you want. An alternative way would be to replace
(for/list ([i x]) ... (add1 i) ...)
with
(for/list ([i (in-range 1 (add1 x)]) ... i ...)
)
Code:
(define (foo-makey const max data)
(let* ((i (length data))
(newy (- max i))
(newpair (cons const newy)))
(if (= max i)
data
(foo-makey const max
(cons newpair data)))))
(define (foo-makex xmax ymax data)
(let* ((i (length data))
(newx (- xmax i)))
(if (= xmax i)
data
(foo-makex xmax ymax
(cons (foo-makey newx ymax '()) data)))))
(define (foo x y)
(foo-makex y x '()))
Output:
> (foo 3 2)
'(((1 . 1) (1 . 2) (1 . 3)) ((2 . 1) (2 . 2) (2 . 3)))
I can't answer your question as-is because I don't understand how the nested lists should work for >2 arguments. AFAIK there is no built-in function to do what you want.
To start you off, here is some code that generates output without nested lists. As an exercise try adjusting the code to do the nested listing. And see if there's a way you can make the code more efficient.
;;can take in any number of arguments
(define (permutations . nums)
(foldl
(lambda (current-num acc)
(append-map
(lambda (list-in-acc)
(for/list ((i (build-list current-num (curry + 1))))
(append list-in-acc (list i))))
acc))
(list (list))
(reverse nums)))
Example 1:
> (permutations 3 2)
'((1 1) (1 2) (1 3) (2 1) (2 2) (2 3))
Example 2:
> (permutations 10)
'((1) (2) (3) (4) (5) (6) (7) (8) (9) (10))
Example 3:
> (permutations 2 3 4)
'((1 1 1)
(1 1 2)
(1 2 1)
(1 2 2)
(1 3 1)
(1 3 2)
(2 1 1)
(2 1 2)
(2 2 1)
(2 2 2)
(2 3 1)
(2 3 2)
(3 1 1)
(3 1 2)
(3 2 1)
(3 2 2)
(3 3 1)
(3 3 2)
(4 1 1)
(4 1 2)
(4 2 1)
(4 2 2)
(4 3 1)
(4 3 2))
(define (build-2d row col)
(build-list row (lambda(x) (build-list col (lambda(y) (list (+ x 1) (+ y 1))))))