I have to combine a list of words to produce a para. I managed following:
(define (wordlist2para wl)
(define str " ")
(for ((w wl))
(set! str (string-append str w " ")))
(string-trim str))
(wordlist2para '("this" "is" "a" "test"))
Output:
"this is a test"
It works but it is not functional. How can I write functional code for this?
If I wanted to do it explicitly and not use string-join, I would recurse and use three cases:
The empty list produces the empty string
A one-element list produces its sole element (this avoids having a trailing separator)
Otherwise, append the car and a space to the recursion on the cdr.
Like this:
(define (wordlist2para ws)
(cond ((null? ws) "")
((null? (cdr ws)) (car ws))
(else (string-append (car ws) " " (wordlist2para (cdr ws))))))
No need of recursion or loop, there is the primitive function string-join for this (see the manual):
(define (wordlist2para wl)
(string-join wl " "))
(wordlist2para '("this" "is" "a" "test"))
;; -> "this is a test"
We have standard procedures that does this:
;; racket library or srfi/13
(string-join '("this" "is" "it")) ; ==> "this is it"
There is a way to always rewrite these that are quite simple. I'd like to step away from rackets great feature set and just focus on simple scheme with recursive procedures. Notice that in your loop you are changing 2 things wl gets smaller, str gets longer, so lets make that:
; all things that change as arguments
(define (wordlist2para-loop wl str)
(if (null? wl)
str
(wordlist2para-loop (cdr wl)
(string-append str (car wl) " "))))
Now for we just replace the loop:
(define (wordlist2para wl)
(wordlist2para-loop wl ""))
From here on you can move the helper to become local or perhaps make it a named let or any other refactoring, but it doesn't really change the resulting compiled result in an implementation much, just how it looks.
Notice I haven't fixed the bug where there is only one word. (wordlist2para '("this")) ; ==> "this " The result is actually exactly the same as in your, only that it's tail recursive and functional.
I am not sure if following can be called functional but it does use some higher order functions:
(define (wordlist2para wl)
(string-trim
(apply string-append
(map (lambda(x) (string-append x " ")) wl))))
(wordlist2para '("this" "is" "a" "test"))
Output:
"this is a test"
Related
I'm trying to make a Rock Paper Scissors game to help myself lean GNU Guile. I've hit a snag where I get user input, the player's choice in the game. If I set it to a string, then the game works correctly. If I use (read) I get back #f as the type from the look up. I've tried formatting read to try to make it a string, which didn't work.
(define (print a)
(display a)
(newline))
(define choices (make-hash-table 3))
(hashq-set! choices "r" "s")
(hashq-set! choices "s" "p")
(hashq-set! choices "p" "r")
(define (cpu-choice) (list-ref (list "r" "p" "s") (random 3)))
(print "You are playing rock paper scissors.")
(print "Type r for rock, p for paper, and s for scissors.")
(define draw
;; "s" ; This works as a test.
(read (open-input-string (read))) ; Can't get user in as string, so the hashq-ref will work.
)
(define cpu-draw (cpu-choice))
;; debug
(print (format #f "Player enterd ~a" draw))
(print (format #f "Player needs to with ~a" (hashq-ref choices draw))) ; Keeps coming back as #f
(print (format #f "CPU has entered ~a" cpu-draw))
;; norm
(newline)
(when (eq? draw cpu-draw)
(print "There was a tie")
(exit))
(when (eq? (hashq-ref choices draw) cpu-draw)
(print "You have won.")
(exit))
(print "You have failed. The computer won.")
How do I get a string from the user? Maybe something like (str (read)) or (read-string) (reading as a string).
$ guile --version
guile (GNU Guile) 2.0.13
Update
I'd just like to mention that while the answer approved is correct, I didn't understand how Guile/Scheme does strings and symbols when writing this. The only way I got the program to work was to change all the strings in choices and in the cpu-choice list into symbols. Ex:
(hashq-set! choices 'r 's)
(list 'r 'p 's)
Thank you Óscar López for your help.
Unless you surround the input with double quotes, the value that you type will be interpreted as a symbol. Either try this:
(define str (read))
> "hello"
Or this:
(define str (symbol->string (read)))
> hello
Either way, str will now hold an actual string:
str
=> "hello"
I am new to the Scheme language and am currently using the Simply Scheme textbook. I was experimenting with some procedures, and I noticed that when I do these functions (below), it prints words in a "spoken" fashion:
(define (display-all sep . vs)
(for-each display (insert-between sep vs)))
(define (insert-between v xs)
(cond ((null? xs) xs)
((null? (cdr xs)) xs)
(else (cons (car xs)
(cons v (insert-between v (cdr xs)))))))
(Code used from How to 'display' multiple parameters in R5RS Scheme)
Then commanding:
(display-all "" 'w 'o 'w " " 't 'h 'i 's " " 'i 's " " 'c 'o 'o 'l)
The letters are printed one by one as if someone was typing them. I was wondering if there was any way to make it easier for me to input these words to be spoken, instead of inputting letter by letter. I was planning to have something like this:
(define (speak . wds)
(...))
where wds would be a string. In the above example, I would like for it to be like this: (speak "wow this is cool") and return "wow this is cool" but each letter displayed one by one.
Thank you in advance for your help!
How about passing a string as input? there's no need to use variable arguments in this case, please try this:
(define (display-all sep vs)
(for-each display (insert-between sep (string->list vs))))
(display-all "" "wow this is cool")
I have long been trying to find the error, I'm doing a programming language and have the next code, using ragg, I have a syntax-object(resto ...) what has a bracket as data, I transform this syntax-object to a datum:
(let ([i (syntax->datum #'(resto ...))])
(display "Content i:")
(display i)
(if (eq? i (string->symbol "(})"))
(display "true")
(display "false")
)
)
and the output is:
Content: (})
false
But if I do this
(for ([i (syntax->datum #'(resto ...))])
(displayln "Content:")
(displayln i)
(if (eq? i (string->symbol "}"))
(display "true")
(display "false")
)
)
and the output is:
Content: }
true
MY QUESTION:
¿WHY THE IF OF CLAUSE LET IS FALSE?
¿AS I CAN COMPARE THESE TWO TYPES AND THAT THE RESULT IS TRUE WITHOUT THE FOR?
Documentation about functions:
syntax->datum
Each piece of code is doing a very different thing, I'll show you how to make each one work. The first one uses let to assign into a variable the whole list returned by syntax->datum, and afterwards you compare it against another list (better use equal? for testing equality, it's more general):
(let ([i (syntax->datum #'(|}|))]) ; `i` contains `(})`
(display "Content i: ")
(displayln i)
(if (equal? i '(|}|)) ; a list with a single element, the symbol `}`
(displayln "true")
(displayln "false")))
The second piece of code is using for to iterate over each element in a list, until it finds the symbol }:
(for ([i (syntax->datum #'(|}|))]) ; `i` contains `}`
(display "Content i: ")
(displayln i)
(if (equal? i '|}|) ; the symbol `}`
(displayln "true")
(displayln "false")))
As a side note, you have to be very careful with the way you're going to process all those curly brackets {} in your language, they're interpreted as normal parentheses () in Racket and they'll be tricky to handle, notice how I had to escape them by surrounding them with vertical bars.
In R5RS Scheme how do you display multiple parameters, with a single call? my implementation below works, but adds extra parentheses and spaces.
#!/usr/bin/env racket
#lang r5rs
(define (display-all . rest) (display rest))
(display-all "I " "have " "a " "lovely " "bunch " "of " "coconuts\n")
results in
owner#K53TA:~$ ./Template.ss
(I have a lovely bunch of coconuts
)
Simplest:
(define (display-all . vs)
(for-each display vs))
Note the use of for-each instead of map - for-each is the same thing but assumes you're only calling the function for side-effects, so instead of returning a list of results (using map with display would return a list of voids) it just returns void.
This can get annoying if you want to display non-string things and have spacing between them, for instance if you want (display-all 12 "bananas") to display the string "12 bananas" you have to manually turn the number into a string and add the space yourself. It would be easier to just add spaces in-between elements of the list:
(define (insert-between v xs)
(cond ((null? xs) xs)
((null? (cdr xs)) xs)
(else (cons (car xs)
(cons v (insert-between v (cdr xs)))))))
(define (display-all . vs)
(for-each display (insert-between " " vs)))
Now calling this:
(display-all "blah" 4 "bloo")
does what you'd expect. If you don't want the spaces inserted automatically, you can specify another argument as the separator object and use it however you need. Here's a version that accepts a separator object:
(define (display-all sep . vs)
(for-each display (insert-between sep vs)))
This approach would make more sense in a version of scheme that supports optional and keyword arguments however, so you could default it to either a space or the empty string and not interfere with the rest-args.
If all the arguments are string, just use apply and string-append. both are in r5rs
#!/usr/bin/env racket
#lang r5rs
(define (display-all . rest) (display (apply string-append rest)))
(display-all "I " "have " "a " "lovely " "bunch " "of " "coconuts\n")
So I'm playing around with a simple doc-string system as a warmup in scheme, the idea being you could do something like:
(def-with-doc (foo a b)
(desc "Takes two parameters and sums them")
(param 'a "First parameter")
(param 'b "Second parameter")
(return "Sum of arguments")
(+ a b)
Which would be turned into:
(begin
(begin
(desc 'foo "Takes two parameters and sums them")
(param 'foo 'a "First parameter")
(param 'foo 'b "Second parameter")
(return 'foo "Sum of arguments"))
(begin
(define (foo a b)
(+ a b))))
The macro I've written:
(define doc-symbol-list '(param desc return))
(define-macro (def-with-doc arg-list #!rest body)
;; Loop over body, splitting into doc calls and everything else
(let loop ((remaining body) (docs '()) (main '()))
(if (null? remaining)
; Reverse accumulation order of docs and main
; And build re-ordered begin tree
(let ((docs (cons 'begin (reverse docs)))
(main (cons 'begin (reverse main))))
(cons 'begin `(,docs ,`(define ,arg-list ,main))))
; Accumulate into docs list if expression is reserved
; Otherwise into the body list
(let ((sexp (car remaining)) (rest (cdr remaining)))
(if (member (car sexp) doc-symbol-list)
(loop rest (cons sexp docs) main)
(loop rest docs (cons sexp main)))))))
Takes the definition, moves the param/desc/return calls into the top level wrapped in begin statements and reconstructs the body of the function, that way the doc string calls are only executed once when the file is loaded rather than each time the function is called. I know I could manually put the doc-string stuff at the top level but I'm trying to emulate Python doc-strings.
Anyhow, the last think that I need to do is bind the function name (foo in above) into the doc-string calls, so that (param 'a "First parameter") becomes (param 'foo 'a "First parameter") so that the function each call is associated with is known. This is where I'm having trouble, every attempt I've made has failed to do what I want.
I would suggest using define-syntax as it is hygienic and its syntax-rules are pretty easy to understand. syntax-rules are in a pattern-to-result format; if you can understand cond, you can understand syntax-rules.
I think this does what you want, judging by the before and after snippets.
(define-syntax def-with-doc
(syntax-rules ()
;; this pattern
[(_ (func params ...)
(tag attributes ...)
...
code)
;; is converted into
(begin
(tag (quote func) attributes ...)
...
(define (func params ...)
code))]))
Forgive my terminology because I've never used doc-strings.
Basically, this matches against anything that follows that pattern of a function + params def, 0 or more tags with attributes, and a code statement.
Then, it just rearranges everything.