How to read a string to get user input in GNU Guile? - scheme

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"

Related

How do you display letters one by one in Scheme?

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")

How do I set a variable to a function that gets input from the user in Racket?

I'm trying to make a program that takes user input at a variety of steps. In the beginning of the program, the player names themselves through an external function that should return a player object, which will be referenced many times later.
#lang racket
(define player
(class object%
(init-field
name
money
lives
health)
(super-new)
(define/public (get-money)
money)
)
)
;None of this function runs/the user is never prompted...
(define (start-game)
(print "What do you want to name yourself?")
(define name (read-line))
(define start-mon (random 1000 5000))
(print "You're starting with ")
(print start-mon)
(print " dollars.")
(new player [name name]
[money 0]
[lives 2]
[health 100])
)
(define (main)
(define player start-game)
(let/ec break
(let loop ()
(print ":")
(define input (read-line))
(cond [(string=? input "health") (send player get-health)]
[(string=? input "exit") (break)]
[else (print "sorry, command not recognized. Type help for commands.")])
(loop)))
(print "exiting..."))
(main)
My question is, how do I correctly assign the player variable to the return object of the function start-game, while letting that whole function execute and prompt the user?
Thank you!

Combining words list to a para in Racket

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"

Scheme case error

I'm trying to create a word count program in Scheme. I think I've worked out an algorithm that'll count my lines, words, and chars, but when I start to run the program, it tells me "The object #\1 is not applicable." "1" is the first character in the file I'm reading, and it should fall under "else". Everything I look at matches my case statement, so I think I'm doing it right, but clearly something's messed up somewhere. Thank you for your help!
(define files
(lambda (reading n)
(begin
(define in (open-input-file reading))
(let loop ((lines 0)
(words 0)
(chars 0)
(port (read-char in)))
(case (port)
((#\newline)
(loop (+ lines 1) words (+ chars 1) (read-char in)))
((#\space #\tab)
(loop lines (+ words 1) (+ chars 1) (read-char in)))
(else (loop lines words (+ chars 1) (read-char in)))))
(close-input-port in)
(display lines)
(display " ")
(display words)
(display " ")
(display chars)
(newline)
(display "Top ")
(display n)
(display " word(s):")
(newline)
'())))
Your problem is fortunately easy to fix. You've written:
(case (port) ...)
but that does a case on the result of calling the function port. Of course, port isn't a function, it's a character, so you just want:
(case port ...)
How does the "let loop" know when you've reached the end of the file? What does read-char return when it hits the end? Hint: read about the eof-object? predicate. A predicate is a function that returns #t or #f. You may need to use a cond rather than a case to use this predicate
Also, the lines, chars and words variables are local to the named let, so you can't print then out "outside". (Hint: print them inside the loop when (eof-object? port) returns #t.
Style quibble: don't use the name "port" for the char that read-char returns. "in" is the port (file handle), Maybe you can use "ch" instead of "port".

How do I compare the symbol a with the character a?

I have a list containing letters.
When I do (car '(a)) it gives me the symbol a.
How do I compare it to the character a?
Must I do (eq? (car list) (car '(a))?
Symbols and characters are different kinds of data. Fortunately, Scheme is willing to let you convert nearly anything you want. In Racket, for instance:
#lang racket
;; the symbol a:
'a
;; the character a:
#\a
;; are they equal? no.
(equal? 'a #\a) ;; produces #f
;; converting a character to a symbol:
(define (char->symbol ch)
(string->symbol (string ch)))
(char->symbol #\a) ;;=> produces 'a
;; converting a symbol to a character
(define (symbol->char sym)
(match (string->list (symbol->string sym))
[(list ch) ch]
[other (error 'symbol->char
"expected a one-character symbol, got: ~s" sym)]))
(symbol->char 'a) ;; => produces #\a
With all that said, if you're working on a homework assignment, the instructor almost certainly has an easier path in mind for you.

Resources