Racket - how to change property value of an struct - racket

I have the following struct:
(define-struct my-struct (label value))
I want to change the property value of all items in my list. I want to set the value 2 in all items.
(define (change-value mylist priority)
( cond
[( empty? mylist) mylist]
[else ( cons ((struct-copy my-struct (first mylist) [value 2]) ) (change-value (rest mylist) value) )]))
)
I am trying to use struct-copy but I am getting the following error:
struct-copy: this function is not defined
Any idea why I am getting this error? Should I import any library?

It's not entirely clear what you intended your code to do. Here's a version that traverses the list of "my-struct" and sets all the "value" properties to 2:
#lang racket
(define-struct my-struct (label value) #:transparent)
(define (change-value mylist)
(for/fold ([result '()])
([s (in-list mylist)])
(cons (struct-copy my-struct s [value 2]) result)))
If I run it I get:
> (define l (list (my-struct 1 3) (my-struct 4 6) (my-struct 8 7)))
> l
(list (my-struct 1 3) (my-struct 4 6) (my-struct 8 7))
>
> (change-value l)
(list (my-struct 8 2) (my-struct 4 2) (my-struct 1 2))

You can change struct by build another struct e.g. if we want to switch element inside struct.
#lang racket
(define-struct s (s1 s2) #:transparent #:mutable)
(define (switch st)
(s (s-s2 st) (s-s1 st)))
;;; TEST
(switch (s 1 2)) ; (s 2 1)
So in here we can use map change every second element inside struct.
#lang racket
(define-struct my-struct (label value) #:transparent #:mutable)
(define (change-value->2 lst-of-struct new-value)
(map (λ (s) (my-struct (my-struct-label s) new-value))
lst-of-struct))
;;; TEST
(change-value->2 (list (my-struct 'a 1) (my-struct 'b 1) (my-struct 'c 1))
2)

Related

typed/racket + racket interoperability

How can I use typed Racket for some functions in my codebase, but use (untyped) Racket for others? When I define a function in Racket but import it into a typed Racket context, it seems to be changing the behavior of the function (functions described below).
As it is now, the files do typecheck, but don't pass my tests in p11typedtest.rkt -- however, my files do successfully pass my tests if I either (A) switch p11typed.rkt to regular Racket or (B) copy the pack function into p11typed.rkt and provide its type annotation.
;; p09.rkt
#lang racket
(provide pack)
;; packs consecutive duplicates within a list into sublists
(define (pack lst)
(for/foldr ([acc '()]) ([x lst])
(match acc
[(cons (cons y ys) zs) #:when (equal? x y)
(list* (list* x y ys) zs)]
[_ (list* (list x) acc)]
)))
;; p11typed.rkt
#lang typed/racket
(provide encode-runlen-mod)
;; (require (only-in (file "p09.rkt") pack))
(require/typed (only-in (file "p09.rkt") pack)
[pack (All (A) (-> (Listof A) (Listof (Listof A))))]
)
(define-type (Runof a) (List Index a))
(define-type (RunListof a) (Listof (U a (Runof a))))
;; encodes a list as a list of runs
(: encode-runlen-mod (All (A) (-> (Listof A) (RunListof A))))
(define (encode-runlen-mod lst)
;; uncomment to print the result of pack
;; (displayln (pack lst))
(for/list ([dups (pack lst)])
(match (length dups)
[1 (car dups)]
[n (list n (car dups))]
)))
; (: pack (All (A) (-> (Listof A) (Listof (Listof A)))))
; (define (pack lst)
; (for/foldr ([acc '()]) ([x lst])
; (match acc
; [(cons (cons y ys) zs) #:when (equal? x y)
; (list* (list* x y ys) zs)]
; [_ (list* (list x) acc)]
; )))
;; p11typedtest.rkt
#lang racket
(require (only-in (file "p11typed.rkt") encode-runlen-mod))
(define (test-output namespace expr v)
(let* ([val (eval expr namespace)]
[fail (not (equal? val v))])
(begin
(display (if fail "FAIL" "ok "))
(display " '(=? ")
(print expr)
(display " ")
(print v)
(display ")'")
(if fail
(begin
(display ", got ")
(print val)
(displayln " instead")
)
(displayln "")
)
(void))
))
(define-namespace-anchor a)
(define ns (namespace-anchor->namespace a))
(test-output ns '(encode-runlen-mod '(1 2 3 4)) '(1 2 3 4))
(test-output ns '(encode-runlen-mod '(1 1 1)) '((3 1)))
(test-output ns '(encode-runlen-mod '(1 2 2 3 4)) '(1 (2 2) 3 4))
(test-output ns '(encode-runlen-mod '(1 2 3 4 4 4)) '(1 2 3 (3 4)))
(test-output ns '(encode-runlen-mod '(1 2 3 (3 4))) '(1 2 3 (3 4)))
(test-output ns '(encode-runlen-mod '(A A A A B C C A A D E E))
'((4 A) B (2 C) (2 A) D (2 E)))
)

How to double a list using tail recursive?

(define (lst-double-helper lst acc)
(if (empty? list)
acc
(lst-double-helper (rest lst) (cons (* (first lst) 2) acc))))
(define (lst-double lst)
(lst-double-helper lst '()))
I feel I'm doing it in the right way. But this gives me an error
(lst-double '(1,2,3))
*: contract violation
expected: number?
given: ',2
argument position: 1st
other arguments...:
Why do it expect the second argument to be a number?
A couple of comments:
List elements are separated by spaces, not commas. That's the error being reported.
The base case of the recursion must refer to the parameter lst, not to list.
Your tail-recursive solution reverses the list, an extra reverse is needed at the end to restore the original order
With the above changes in place, it works as expected:
(define (lst-double-helper lst acc)
(if (empty? lst) ; parameter is called `lst`
acc
(lst-double-helper (rest lst) (cons (* (first lst) 2) acc))))
(define (lst-double lst)
(reverse ; required to restore original order
(lst-double-helper lst '())))
(lst-double '(1 2 3)) ; use spaces to separate elements
=> '(2 4 6)
Be aware that a tail-recursive solution that traverses an input list and conses its elements to build an output list, will necessarily reverse the order of the elements in the input list. This is ok, and it's normal to do a reverse at the end. Possible alternatives to avoid reversing the elements at the end would be to reverse the input list at the beginning or to write a non-tail-recusive solution.
One such way is by using continuation-passing style. Here we add a parameter named return which effectively encodes a return-like behavior with a lambda. double now takes two arguments: the list to double, xs, and the continuation of the result, return –
(define (double xs return)
(if (empty? xs)
(return empty)
(double (cdr xs)
(lambda (result)
(return (cons (* 2 (car xs))
result))))))
As an example, the result of double applied to a list of '(1 2 3) is sent to print
(double '(1 2 3) print)
;; '(2 4 6)
;; => #<void>
double evaluates to whatever the final continuation evaluates to; in this case, print evaluates to #<void>. We can use the identity function to effectively get the value out –
(double '(1 2 3) identity)
;; => '(2 4 6)
Racket allows you to easily specify default arguments, so we can modify double to use identity as the default continuation
(define (double xs (return identity))
;; ...
)
This style results in convenient programs that work in two call styles at simultaneously: continuation-passing style –
(double '(10 11 12) print)
;; '(20 22 24)
;; => #<void>
(double '(10 11 12) length)
;; => 3
(double '(10 11 12) car)
;; => 20
(double '(10 11 12) cdr)
;; => '(22 24)
... or in direct style, using the default identity continuation
(print (double '(10 11 12)))
;; '(20 22 24)
(length (double '(10 11 12)))
;; => 3
(car (double '(10 11 12)))
;; => 20
(cdr (double '(10 11 12)))
;; => '(22 24)
use map.
(map (lambda (a) (* a 2)) '(1 2 3))
For nested lists:
(define (atom? x)
(and (not (null? x))
(not (pair? x))))
(define (lst-double-helper lst acc)
(cond ((empty? lst) acc)
((atom? (car lst)) (lst-double-helper (rest lst) (cons (* (first lst) 2) acc)))
(else (lst-double-helper (rest lst) (cons (lst-double (first lst))
acc) ))))
(define (lst-double lst)
(reverse ; required to restore original order
(lst-double-helper lst '())))
but actually to make this function tail-recursive is a little bit meaningless,
because as #simmone mentioned, map would do it
(define (list-doubler lst)
(map (lambda (x) (* 2 x)) lst))
(list-doubler '(1 2 3))
;; '(2 4 6)

LISP List Not Appending And Printing Twice

I'm quite new to LISP and I am trying to work on the cond statement for class. Currently, I am attempting to check if the value passed is a list and if so, append the letter d onto the list.
Here is my code:
(defun test(L)
(listp L)
(cond ((listp L) (append L (list 'd)))
)
(write L)
)
(test (list 'a 'b 'c))
The output I get is:
(A B C)
(A B C)
If I change the test to: (test (car(list 'a 'b 'c)))
The new output I get is:
A
A
Two things I am wondering is
1.) Why isn't D appended onto the list if the first test passes a list?
2.) Why are they being printed twice? I'm using LISP Works so I figure it's actually something with how it always outputs the final value or something.
1.) The same reason str + "d" doesn't mutate str in Java or Python. It creates a new list that you do not use!
>>> str + "d"
'abcd'
>>> str
'abc'
Crazy similar isn't it?
2.) In CL the return is the last evaluated expression. The REPL prints every top level expression result to the terminal. Python does this too:
>>> def test():
... x = 2 + 3
... print x
... return x
...
>>> test()
5
5
Update
How to mutate the argument list. The simple answer is that you need to mutate the last pair of the argument instead:
(defun test (l)
(assert (consp 1) (l) "l needs to be a non nil list. Got: ~a" l)
(nconc l (list 'd)
(write l)))
(defparameter *test1* (list 1 2 3))
(defparameter *test1-copy* *test1*)
(test *test1*) ; ==> (1 2 3 d) (and prints (1 2 3 d))
*test1* ; ==> (1 2 3 d)
*test1-copy* ; ==> (1 2 3 d)
(eq *test1* *test1-copy*) ; ==> t
(test '())
** error l needs to be a non nil list. Got: NIL
(nconc l x) does (setf (cdr (last l)) x)
If you need to alter the binding, then you need to make a macro:
(defmacro testm (var)
(assert (symbolp var) (var) "List needs to be a variable binding. Got: ~a" var)
`(progn
(when (listp ,var)
(setf ,var (append ,var (list 'd)))
(write ,var))))
(macroexpand '(testm *test2*))
; ==> (progn
; (when (consp *test2*)
; (setf *test2* (append *test2* (list 'd))))
; (write *test2*))
(defparameter *test2* (list 1 2 3))
(defparameter *test2-copy* *test2*)
(testm *test2*) ; ==> (1 2 3 d) (and prints (1 2 3 d))
*test2* ; ==> (1 2 3 d)
*test2-copy* ; ==> (1 2 3)
(eq *test2* *test2-copy*) ; ==> nil
(defparameter *x* nil)
(testm *x*) ; ==> (d) (and prints (d))
*x* ; ==> (d)
(testm '(1))
** error List needs to be a variable binding. Got: '(1)
Idiomatic way to do it
(defun test (list)
(if (consp list)
(append list '(d))
list))
(write (test '(1 2 3)))
; ==> (1 2 3 d) (and prints (1 2 3 d))
(defparameter *test3* '(1 2 3))
(setf *test3* (test *test3*))
*test3* ; ==> (1 2 3 d)

Looping over a list and generate serial statements in a lambda

I have a macro called compare-and-swap!:
(define-macro (compare-and-swap! l x y)
`(if (> (vector-ref ,l ,x) (vector-ref ,l ,y))
(vector-swap! ,l ,x ,y)))
It works, I'm testing it like this:
(define v (list->vector '(5 4 3 2 1)))
(print v)
(compare-and-swap! v 1 2)
(print v)
I have a function that returns a list of pairs that I can call compare-and-swap! on serially to sort the whole list:
(batcher 8) → ((0 1) (2 3) (0 2) (1 3) (1 2) (4 5) (6 7) (4 6) (5 7) (5 6) (0 4) (2 6) (2 4) (1 5) (3 7) (3 5) (1 2) (3 4) (5 6))
Now I wish to create a macro that generates a lambda that sorts an N element list by calling batcher and doing the compare-and-swap! for each pair.
For example,
(generate-sorter 8)
→
(lambda (l) (begin (compare-and-swap! l 0 1) (compare-and-swap! l 2 3) ...))
→
(lambda (l) (begin (if (> (vector-ref l 0) (vector-ref l 1)) (vector-swap! 0 1)) (if (> (vector-ref l 2) (vector-ref l 3)) (vector-swap! 2 3))) ... )
I made a function that generates the necessary code:
(define generate-sorter (lambda (len)
(list 'lambda '( li ) 'begin (map (lambda (pair) (list 'compare-and-swap! 'li (first pair) (second pair))) (batcher len)))
))
But I don't now how to make it into a macro.
You don't need a macro for this and, in particular, for the 'generate' part. I suspect that you were thinking macro because the result of generate-sorter can vary from call to call and you hoped to encode the result through macro expansion. An alternative is to capture the result in the lexical environment as such:
(define-syntax compare-and-swap!
(syntax-rules ()
((_ l x y)
(when (> (vector-ref l x) (vector-ref l y))
(vector-swap! l x y)))))
(define (generate-sorter n)
(let ((sorters (generate-sorter n)))
(lambda (l)
(for-each (lambda (sorter)
(compare-and-swap! l (car sorter) (card sorter)))
sorters))))
(define sorter-8 (generate-sorter 8))
(sorter-8 <l-thingy>)
-> <sorted-l-thingy>

Recursing Through Nested List LISP

How would I recurse through nested lists?
For example, given: '((A 1 2) (B 3 4))
How would I add 2 to the second element in each nested sublist?
(defun get-p0 (points)
(loop for x from 0 to
(- (list-length points) 1) do
(+ 2 (cadr (nth x points)))
)
)
I'm not really sure why (get-p0 '((A 1 2) (B 3 4))) returns NIL.
I'd go with something like this:
(loop for (letter x y) in '((A 1 2) (B 3 4))
collect (list letter (+ 2 x) y))
The reason: it's shorter and you don't measure the length of the list in order to iterate over it (why would you do that?)
Since you ask for a recursive solution:
(defun get-p0 (lst &optional (n 0))
(if (null lst)
nil
(let ((elt1 (first lst)) (eltn (cdr lst)))
(if (listp elt1)
(cons (get-p0 elt1) (get-p0 eltn))
(cons (if (= n 1) (+ elt1 2) elt1) (get-p0 eltn (+ n 1)))))))
so
? (get-p0 '((A 1 2) (B 3 4)))
((A 3 2) (B 5 4))
and it recurses further down if necessary:
? (get-p0 '((A 0 2) ((B -4 4) (C 10 4))))
((A 2 2) ((B -2 4) (C 12 4)))
The way you put it, you can consider the problem as a basic recursion pattern: you go through a list using recursion or iteration (mapcar, reduce, etc.; dolist, loop, etc.) and apply a function to its entries. Here is a functional solution:
(defun get-p0 (points)
(mapcar #'add-2 points))
where the auxiliary function can be defined as follows:
(defun add-2 (lst)
"Add 2 to the 2nd item"
(let ((res '()))
(do ((l lst (cdr l))
(i 1 (1+ i)))
((null l) (nreverse res))
(push (if (= 2 i)
(+ 2 (car l))
(car l))
res))))
As written your 'loop' use does not return anything; thus NIL is returned. As is your code is simply iterating over x and computing something; that something isn't stored anywhere.
So, how to get your desired result? Assuming you are willing to modify each point in points, this should work:
(defun get-p0 (points)
(loop for x from 0 to (- (list-length points) 1) do
(let ((point (nth x points)))
(setf (cadr point) (+ 2 (cadr point)))))
points)