Replacing a String in a List in Racket - scheme

I am trying to replace a string in the list with another given string, using abstract list functions and lambda only.
The function consumes lst, a list of strings, str, the string you are replacing, and rep, the string you are replacing str with.
Here is an example:
(replace (list "hi" "how" "are" "you") "hi" "bye") -> (list "bye" "how" "are" "you")
Written below is the code that I wrote in recursion and it works.
(define (replace lst str rep)
(cond [(empty? lst) empty]
[(equal? match (first lst))
(cons rep (replace-all (rest lst) match rep))]
[else (cons (first lst) (replace-all (rest lst) match rep))]))
Below that code is what I have tried but I'm not sure how to fix it to make it produce what I want.
(define (replace lst str rep)
(map (lambda (x) (string=? x str)) lst))
Any and all help is appreciated, thanks!

Almost there! you just have to ask, for each string: is this the one I want to replace? then replace it - otherwise leave it untouched:
(define (replace lst str rep)
(map (lambda (x) (if (string=? x str) rep x))
lst))

Related

scheme, sicp, solution 3.19, procedure with infinite loop works in case it is provided as argument

could someone help me with clarification to one of the possible solution to exercise 3.19. the procedure mystery is infinite loop in case list cycle is given as argument. nevertheless when we use procedure eq? to check if list contains the cycle, it works and provides true value.
(define (last-pair x)
(if (null? (cdr x))
x
(last-pair (cdr x))
)
)
(define (make-cycle x)
(set-cdr! (last-pair x) x)
)
(define (mystery x)
(define (loop x y)
(if (null? x)
y
(let ((temp (cdr x)))
(set-cdr! x y)
(loop temp x)
)
)
)
(loop x '())
)
(define t (list 1 2 3))
(define w (make-cycle t))
(eq? (mystery t) t)
it looks like magic. I would appreciate for any help.
mystery reverses an array "in-place" by repeatedly snipping off the cdr of each entry and replacing that with the cdr of the previous x.
If this list has no loop, then it will end up reversed by the time you get back to the original '(). If there is a loop, you'll have the original array's pointer.
This is definitely a tricky to understand issue. If you make a box-and-pointer diagram it will definitely help and you'll only need to draw 3 diagrams.
Automatically Generating Diagrams of Lists
In the process of doing SICP myself, I found myself wanting a way to visualize list mutation (and to skip the numerous "draw a list diagram of..." exercises). I wrote a small function for doing so and I thought you might find it helpful if I shared it.
These diagrams are an example of this function being run on x each time loop (within the mystery function) is ran.
The following code is what I used for generating these diagrams. I wrote this code as a Scheme novice, but it's very simple to use: the function (list->graphviz) accepts a parameter lst which is the list you'd like a diagram of, as well as an optional argument graph-name which gives the graph a special name.
(define* (list->graphviz lst #:optional graph-name)
"""Convert a list into a set of Graphviz instructions
`lst' is the list you'd like a diagram of
`graph-name` is an optional parameter indicating the name you'd like to give the graph."""
(define number 0)
(define result "")
(define ordinals '())
(define (result-append! str)
(set! result (string-append result str)))
(define* (nodename n #:optional cell)
(format #f "cons~a~a" n (if cell (string-append ":" cell) "")))
(define* (build-connector from to #:optional from-cell)
(format #f "\t~a -> ~a;~%" (nodename from from-cell) (nodename to)))
(define (build-shape elt)
(define (build-label cell)
(cond ((null? cell) "/");; "∅") ; null character
((pair? cell) "*");; "•") ; bullet dot character
(else (format #f "~a" cell))))
(set! number (+ number 1))
(format #f "\t~a [shape=record,label=\"<car> ~a | <cdr> ~a\"];~%"
(nodename number)
(build-label (car elt))
(build-label (cdr elt))))
(define* (search xs #:optional from-id from-cell)
(let ((existing (assq xs ordinals)))
(cond
;; if we're not dealing with a pair, don't bother making a shape
((not (pair? xs)) (result-append! "\tnothing [shape=polygon, label=\"not a pair\"]\n"))
((pair? existing)
(result-append! (build-connector from-id (cdr existing) from-cell)))
(else
(begin
(result-append! (build-shape xs))
(set! ordinals (assq-set! ordinals xs number))
(let ((parent-id number))
;; make a X->Y connector
(if (number? from-id)
(result-append! (build-connector from-id parent-id from-cell)))
;; recurse
(if (pair? (car xs)) (search (car xs) parent-id "car"))
(if (pair? (cdr xs)) (search (cdr xs) parent-id "cdr"))))))))
(search lst)
(string-append "digraph " graph-name " {\n" result "}\n"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;; Here is where `mystery' begins ;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define t '(1 2 3))
(set-cdr! (cddr t) t)
(define (mystery x)
(define (loop x y graph-num)
(display (list->graphviz x (format #f "graph~a" graph-num)))
(if (null? x)
y
(let ((temp (cdr x)))
(set-cdr! x y)
(loop temp x (+ 1 graph-num)))))
(loop x '() 0))
(mystery t)
The code above code generates Graphviz graph description statements, which must then be processed by dot (Graphviz) to be rendered to a graphical format.
For example, you can run the code above and pipe it into dot:
$ scheme generate_box_ptr.scm | dot -o ptrs.ps -Tps
This command generates a postscript file which has the advantage of separating each list into it's own page if you've run list->graphviz more than once. dot can also output PNGs, PDFs and many other file formats as the manpage describes.

Call of non-procedure in function definition?

I found a short introduction to Scheme online, and I'm having a bit of trouble with this function:
(define (title-style str)
(let loop ((lc #\space) (i 0) (c (string-ref str 0)))
((if (char=? lc #\space)
(string-set! str i (char-upcase c)))
(if (= (- (string-length str) 1) i)
str
(loop c (+ i 1) (string-ref str (+ i 1)))))))
(display "star wars iv: a new hope")
(display (title-style "star wars iv: a new hope"))
When I try calling it, I get this:
Error: call of non-procedure: #<unspecified>
Call history:
title-style.scm:6: loop
...
title-style.scm:1: g6 <--
That error comes from Chicken Scheme, I am also getting the same results in Chez Scheme.
It converts a string to title case, and from the error messages I got earlier, it does: call of non-procedure: "Star Wars Iv: A New Hope"
I understand what you intend to do, but that's not the right way to structure a conditional expression in Scheme. Also, right before the first if there's a misplaced opening parentheses (which is the one causing the reported error), and you have to advance the recursion in all cases. This should work for non-empty strings:
(define (title-style str)
(let loop ((lc #\space) (i 0) (c (string-ref str 0)))
(cond ((= (- (string-length str) 1) i)
str)
((char=? lc #\space)
(string-set! str i (char-upcase c))
(loop c (+ i 1) (string-ref str (+ i 1))))
(else
(loop c (+ i 1) (string-ref str (+ i 1)))))))
But still, it's not the recommended way to write a solution in Scheme, you're mutating the input string along the way, which is discouraged, and you're thinking in terms of indexes. Besides, you're imposing an extra restriction on your input: that the strings must be mutable, and not all Scheme dialects support this by default.
A functional tail-recursive style is preferred, where we create a new string as output, leaving the original input untouched and leveraging the rich library of list procedures available in the language; this is what I mean:
(define (title-style str)
(let loop ((lc #\space) (lst (string->list str)) (acc '()))
(cond ((null? lst)
(list->string (reverse acc)))
((char=? lc #\space)
(loop (car lst) (cdr lst) (cons (char-upcase (car lst)) acc)))
(else
(loop (car lst) (cdr lst) (cons (car lst) acc))))))
Either way, it works as expected:
(title-style "star wars iv: a new hope")
=> "Star Wars Iv: A New Hope"

Why won't this Scheme function compile?

I have this scheme function that I'm supposed to run lists of integers on but I have no idea what the error means. The error states: "if: expected a question and two answers, but found 4 parts in: (if(null? list) '() (cons (+ 1 (car list)) (f(cdr list)))). What is missing from this function and what on earth does '() do? Thanks! I've never used Scheme before.
(define (f list)
(if (null? list)
’()
(cons (+ 1 (car list)) (f (cdr list)))))
The character you are using to quote the empty list is the right quotation mark ’, U+2019. Your code works fine if you use the ascii apostrophe ', U+0027
(define (f list)
(if (null? list)
'()
(cons (+ 1 (car list)) (f (cdr list)))))
You used the wrong quote (maybe a copy paste error?).
Use ' not ’.

Racket/Scheme: How to avoid repeating function call in cond

I'm almost embarrassed for asking this Racket/Scheme question, but can anybody tell me how to avoid repeating a function call inside "there" if it was used to determine the condition "here" in the first place? (cond [here there])
What I am trying to get is the equivalent of the following C style code in Racket/Scheme (notice that I only had to call regex() once, since it was stored in a variable match):
// initialiation code
if (match = regex("start", "startofstring")) {
list->push(match);
}
else {
printf("No matching regex\n");
}
The Racket code that I want to avoid is the following since I have to call regexp-match twice:
(cond
[(regexp-match #rx"start" "startofstring")
(set! list (cons (regexp-match #rx"start" "startofstring") list)]
[else
(printf "No matching regex\n")])
Now, I could do:
(define match (regexp-match #rx"start" "startofstring"))
(cond
[match
(set! list (cons match list)]
[else
(printf "No matching regex\n")])
But this approach will mean that I have to define very many variable if I have more than one condition (in my actual code, I have more than one condition... but for the sake of the snippet above, I only put in one). So it will end up looking ugly like this:
(define match1 (regexp-match #rx"start" "startofstring"))
(define match2 (regexp-match #rx"blah" "startofstring"))
....
(define matchn (regexp-match #rx"blahn" "startofstring"))
(cond
[match1
(set! list (cons match1 list)]
[match2
(set! list (cons match2 list)]
....
[matchn
(set! list (cons matchn list)]
[else
(printf "No matching regex\n")])
What I would like is something more along the lines of:
(cond
[(define match (regexp-match #rx"start" "startofstring"))
(set! list (cons match list)]
[(define match (regexp-match #rx"blah" "startofstring"))
(set! list (cons match list)]
...
[(define match (regexp-match #rx"blahn" "startofstring"))
(set! list (cons match list)]
[else
(printf "No matching regex\n")])
But this is obviously a syntax error because (define .. ..) can't be used in the condition "here". Sorry for the lack of clarity... I tried the best I could to convey what I am saying.
I know this is very super simple but I can't quite wrap my head around it (I haven't use languages other than c-style languages).
The correct solution is to use cond's => form:
(cond ((regexp-match #rx"start" "startofstring")
=> (lambda (match)
(set! lst (cons match lst))))
...)
(Note that I renamed your list variable to lst to avoid shadowing the built-in list procedure.)
If you have many patterns, and the same action for multiple patterns, you should extract out the common code into a separate procedure:
(define (push! match)
(set! lst (cons match lst)))
(cond ((regexp-match #rx"start" str) => push!)
((regexp-match #rx"blah" str) => push!)
...)
While the above works, I want to suggest that you don't use set!, since it's not very functional. The standard Scheme way to build up a list from a loop is to use a named let, like so:
(let loop ((result '())
(strs strs))
(define (next match)
(loop (cons match result) (cdr strs)))
(if (null? strs)
result
(let ((str (car strs)))
(cond ((regexp-match #rx"start" str) => next)
...)))
There are a couple of clean ways to do this. First of all, you could use match here.
(match "startofstring"
[(regexp #rx"start" match) (cons match list)]
[(regexp #rx"blah" match) (cons match list)]
...
[_ (printf "No matching regex.\n")])
Of course, even that has a lot of repetition. You could do this a little differently with a small helper function.
(define (any-match str . regexes)
(for/or ([regex (in-list regexes)])
(regexp-match regex str)))
(let ([match (any-match "startofstring"
#rx"start"
#rx"blah")])
(if match
(cons match list)
(printf "No matching regex\n")))
Alternatively, if you don't want the baggage of a helper function, but you're okay with a little bit more repetition, you can just do this:
(let ([match (or (regexp-match #rx"start" "startofstring")
(regexp-match #rx"blah" "startofstring"))])
(if match
(cons match list)
(printf "No matching regex\n")))

about a small program

Consider the following program. It's supposed to take a list of strings, and return a string whose characters are the first character from each string. For instance, (downward (cons "caa" (cons "abbb" empty)) should return "ca". Why do I keep getting an error?
(define (downward l)
(cond
[(empty? l) ""]
[else (substring (first l) 0 1
(downward (rest l)))]))
You're correctly iterating over the input, but forgot to "stick together" each part of the output. In this case, string-append will allow you to put together all the elements of the answer:
(define (downward l)
(cond
[(empty? l) ""]
[else (string-append (substring (first l) 0 1)
(downward (rest l)))]))
This is how it works:
(downward '("caa" "abbb"))
=> "ca"
On second thought, the question is a bit ambiguous. Do you want a string as the output? or a list? If it's a list, you only have to change the base case and the "sticking" procedure - use cons for building lists, in the same way that string-append is useful for building strings:
(define (downward l)
(cond
[(empty? l) empty]
[else (cons (substring (first l) 0 1)
(downward (rest l)))]))
(downward '("caa" "abbb"))
=> '("c" "a")

Resources