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")))
Related
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))
I am trying to count occurence of a list in another one but I am stuck. I don't know if there any function to do that however here is my code. It actually finds the first occurence and return to 1. How can I continue to count?
(define *test* '(a b c a b a b a b c b c b c a b c))
(define match
(lambda (pattern text) (cond ((null? pattern) 1)
((null? text) 0)
((eq? (car text) (car pattern))
(match (cdr pattern) (cdr text)))
(else (match pattern (cdr text))))
))
Your code checks positions in the list until it finds a match, and if it does, it returns that it found a match. You want to check each position in the list and add up the ones that contain the pattern, but since your code looks ahead to find the pattern, it's difficult to control which part of the list it operates on. I couldn't 'fix' the problem in your code, so I decided to write something from scratch. Hopefully it makes sense:
(define begins-with (lambda (starter-list full-list)
(cond ((null? starter-list) #t)
((eq? (car starter-list) (car full-list)) (begins-with (cdr starter-list) (cdr full-list)))
(else #f)
)))
(define count-matches
(lambda (pattern lst)
(cond ((null? lst) 0)
((begins-with pattern lst) (+ 1 (count-matches pattern (cdr lst))))
(else (count-matches pattern (cdr lst)))
The first function, begins-with, doesn't check the whole string for the pattern, it just checks if it "begins with" the pattern. That allows us to use the other function, count-matches, to count the number of suffixes which begin with the pattern, in other words, the number of times the pattern appears in the string.
Note that the code I wrote above will count overlapping sequences, for example '(a b a) appears twice in '(a b a b a).
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.
I had to do a word translator in the school, ive tried it like this in the Programm: DrRacket, but when I translate german into english, it gives me "Error", my teacher also don't knows how to fix the mistake, I hope someone of you can help me.
(define *lex*
'((cat Katze)
(dog Hund)
(eats frisst)
(jumps springt)
(the die)
(the der)))
(define (wordtranslator word *lex*)
(cond ((null? liste) 'Error)
((not (equal? word (or (car (car list)) (car (cdr (car list)))))) (wordtranslator word (cdr liste)))
(else
(cond
((equal? word (car (car list))) (car (cdr (car list))))
((equal? word (car (cdr (car list)))) (car (car list)))))))
when I want to translate "Hund" it shows:
> (wordtranslator 'Hund *lex*)
Error
>
The implementation is unnecessarily complex, and it has a couple of syntax errors - for instance: is the list parameter called *lex*, list or liste? and why nest cond expressions, if a single multi-part cond suffices? also notice that the way you're using or within equal? is wrong. It'd be better to try again from scratch:
(define (wordtranslator word liste)
; if the list is empty, we didn't find the word, signal an error
(cond ((null? liste) 'Error)
; is the word in the first element of pair?
((equal? word (caar liste))
; then return the second element of pair
(cadar liste))
; is the word in the second element of pair?
((equal? word (cadar liste))
; then return the first element of pair
(caar liste))
; otherwise, the word is not in the current pair
(else
; advance the recursion to the next pair
(wordtranslator word (cdr liste)))))
From the comments I understand that you were trying to find first if the word is not in the current pair. This is also possible, but IMHO the code will be less readable and you'll need to use more comparisons; also take notice of the correct way to express the second condition. I'll write this in a way that is similar to what you had in mind, but remember - both implementations are equivalent:
(define (wordtranslator word liste)
(cond ((null? liste) 'Error)
((not (or (equal? word (car (car liste)))
(equal? word (car (cdr (car liste))))))
(wordtranslator word (cdr liste)))
(else
(cond ((equal? word (car (car liste)))
(car (cdr (car liste))))
((equal? word (car (cdr (car liste))))
(car (car liste)))))))
I recommend that you use the first implementation, the else part is already expressing what the complex second condition is doing in the second implementation, see how reordering the conditions can simplify the code. Either way the translation works on both directions:
(wordtranslator 'Hund *lex*)
=> 'dog
(wordtranslator 'dog *lex*)
=> 'Hund
(wordtranslator 'bunny *lex*)
=> 'Error
im trying to load a sxml file... i manage to do that in scheme. now i want to go through it using recursion and located items that i want. my code is like this,
(define file (read(open-input-file "test1.sxml")))
(define myfunc
(lambda (S)
(if (eq? "foo" (car S))
(display "found\n")
(display "not found\n")
)
(if (null? (cdr S))
(display "\n")
(myfunc(cdr S)))))
but it seems that it goes through only the first line of the sxml file. how can i make it go through all the file until the end?
1) You need to search through all of the sublists of the structure. Your code right now only looks at the top-most elements.
2) You usually don't want to have multiple statements in a row (like your two if statements)
3) You probably want to look for symbols, not strings, in your SXML file. Regardless, you must use equal? to compare strings.
Thus, your function becomes
(define myfunc
(lambda (S)
(cond
((null? S) #f)
((pair? (car S)) (or (myfunc (car S)) (myfunc (cdr S)))) ; search on sublists
((equal? "foo" (car S)) #t) ; if found, return true
(else (myfunc (cdr S))))))