I have to learn the basics of Scheme to complete a project, but I'm having a little difficulty constructing the equivalent of a while loop. For instance, in the following code, I want to have the user input a character. If the character is an x, I want to break out of the loop. If it is any other character, the character will be displayed, and the loop proceeds with the next iteration.
(define getUserChar (lambda ()
(define ch)
(let loop ()
(display "Enter character: ")
(set! ch (read-char))
(if (not (char=? ch #\x))
(begin
(display choice)
(loop)
)))))
This technically does work, but it skips an iteration. For example:
Enter character: a
Enter character:
Enter character: s
I am not able to input a character during that second iteration. I can't seem to pinpoint the problem. Any help is very appreciated.
The problem is that you're actually reading two characters. The one you typed, plus the line break. I tried to preserve as much as possible from your original solution, and this is what I came up with:
(define getUserChar
(lambda ()
(define ch #\0)
(let loop ()
(display "Enter character: ")
(set! ch (string-ref (read-line) 0))
(if (not (char=? ch #\x))
(begin
(display ch)
(newline)
(loop))))))
IMHO a more idiomatic solution doesn't require a mutation operation, I prefer this one:
(define (getUserChar)
(let loop ((ch #\0))
(unless (char=? ch #\x)
(display "Enter character: ")
(let ((ch (string-ref (read-line) 0)))
(display ch)
(newline)
(loop ch)))))
This has to do with the fact that you are entering two characters in the very first iteration of the loop, namely "a\n" ("a~n") and that makes the second iteration just take the #\NewLine you entered together with the #\a as the character.
If you want to take a whole line at a time you get-line (R6RS) or read-line (R7RS) instead or look at buffer-mode (R6RS) and slurp the chars before starting the new iteration to continue using chars.
Related
I am having some difficulty printing a string in scheme after using a condition and get the following error:
application: not a procedure;
expected a procedure that can be applied to arguments
given: #t
arguments...:
I can't seem to figure out what is wrong with it and would appreciate any help.
(define (neg int)
(cond
(((< int 0) (display "negative"))
(* int (-1)))))
You have too many brackets at the start of your condition; you need two, not three. Remember that in Scheme, if you surround something between () that becomes a function application! That's why this doesn't make sense: (-1), because -1 is not a function, it's a number. Also, what will you do if the value is not negative? You need to handle that case, too! Try this:
(define (neg int)
(cond ((< int 0)
(display "negative ")
(* int -1)) ; or better: (- int)
(else
(display "positive ")
int)))
If Guile is not the best Scheme for this usage, then which one should I be looking at? I'm basically looking for a Guile equivalent of awk '{print $N}'. If Scheme can't do this, then I'd like to know why not.
Guile changed its I/O a bit between 2.0 and 2.2, so this uses r6rs I/O which (hopefully) works the same in both, but I haven't tested with 2.2.
This can be optimized further.
#!/usr/bin/guile \
-e start -s
!#
(use-modules (rnrs io ports))
;;; Reads one line from current-input-port and prints the field indicated by
;;; field-num. If the line does not have enough fields, it prints a newline.
;;; Returns the field, an empty string, or #f if end of file is reached.
(define (get-field field-num)
(let ((line (get-line (current-input-port))))
(if (eof-object? line)
#f
(let ((fields (string-tokenize line)))
(if (< field-num (length fields))
(let ((field (list-ref fields field-num)))
(put-string (current-output-port)
(string-append field "\n"))
field)
(and (put-string (current-output-port) "\n")
""))))))
;;; Repeat get-field until until end of file is reached
(define (get-all-fields field-num)
(if (get-field field-num)
(get-all-fields field-num)
#f))
(define (start args)
(if (and (> (length args) 1)
(integer? (string->number (list-ref args 1))))
(get-all-fields (1- (string->number (list-ref args 1))))
(display (string-join
`("Usage:" ,(list-ref args 0) "<field-number>\n")
" "))))
At my blog I have an essay giving a set of functions that make it easy to handle delimited text files.
Both of the following code blocks should (in my mind) be infinite loops
This works
(define call/cc call-with-current-continuation)
(define l 0)
(define i 0)
((lambda ()
(call/cc
(lambda (k)
(set! l k)))
(write i)
(newline)
(set! i (+ i 1))
(l "ignore")))
This does not work:
(define call/cc call-with-current-continuation)
(define l 0)
(define i 0)
(begin
(call/cc
(lambda (k)
(set! l k)))
(write i)
(newline)
(set! i (+ i 1))
(l "ignore"))
The only difference is one uses a lambda and one uses a begin block.
Why does the second block of code not work?
Thanks
In the second case, the begin splices its arguments into the top-level. Note that there are two kinds of begin: if it's in an expression position, it just sequences operations one after the other. The second kind (which is what you have) will splice all of its arguments into the surrounding context.
The spliced call/cc expression's continuation is actually the empty continuation, since each top-level expression is evaluated separately (i.e., in the empty continuation). You can check this by putting a let around the begin, which forces it to be in an expression position. Then it will infinite loop like you expect.
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".
What's the best way to read input from stdin in racket?
In particular I'd like something like cin from c++ or scanf from c where I specify the types of things I want read and they are returned.
read-line is easy. To be portable across Unix and Windows, additional option is required.
(read-line (current-input-port) 'any)
Return and linefeed characters are detected after the conversions that
are automatically performed when reading a file in text mode. For
example, reading a file in text mode on Windows automatically changes
return-linefeed combinations to a linefeed. Thus, when a file is
opened in text mode, 'linefeed is usually the appropriate read-line
mode.
So, 'any is required to be portable when the input port is not a file (standard input).
Test program:
#lang racket
(let loop ()
(display "Input: ")
(define a (read-line (current-input-port) 'any))
(printf "input: ~a, length: ~a, last character: ~a\n"
a
(string-length a)
(char->integer (string-ref a (- (string-length a) 1))))
(loop))
In Windows, replace (read-line (current-input-port) 'any) with (read-line) and see what happens.
You can do pretty much everything you want to... at the low level, I would suggest (read-line) and (read-bytes). For higher-level processing (as e.g. scanf does), I would suggest a regexp-match on the input. For instance
(regexp-match #px" *([0-9]+)" (current-input-port))
I'd use the read procedure for the general case. If the data type to be read is known beforehand, use read-char, read-string, read-bytes.
Also, take a look at this implemenation for reading formatted input - a scanf in Scheme.
Here's a basis for line-by-line processing in Racket Scheme. It doesn't split an input line into multiple words, or do typed input, but this seems like a good place to put it.
(define (get)
(read-line (current-input-port)))
(define (put . vs)
(for-each display vs)
(displayln ""))
(define (sed fn)
(let ((line (get)))
(if (not (eof-object? line))
(begin
(fn line)
(sed fn))
'true)))
(sed (lambda (line)
(put "Hello, " line)))
Here's one that does split input, also encodes CSV for good measure.
(define (get)
(read-line (current-input-port)))
(define split string-split)
(define sep ",")
(define enc identity)
(define (enc-csv s)
(string-append "\"" (string-replace s "\"" "\"\"") "\""))
(define enc enc-csv)
(define (put . vs)
(displayln (string-join (map enc vs) sep)))
(define (sed fn)
(let ((line (get)))
(if (not (eof-object? line))
(begin
(fn line)
(sed fn))
'true)))
(sed (lambda (line)
(apply put (split line))))
This works in Racket. I'm not sure how much of it is specific to Racket. It doesn't seem to work in Chicken or Guile.