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".
Related
(define aaa ;;val:string
(lambda(x) ;;x:string
(case
((string=? (substring x 0 1) "+") (aaa(substring x 1)))
((string=? "a" "b")(string-append("-" (aaa(substring x 1)))))
((char=?(string-ref x 0)#\.) (-404))
(else
(if (= (findpoint x) -1)
"a"
"b"
)
)
)
)
)
Hello, I have a problem with DrRacket:
When I try to run this code, it gives me the error:
case: expected a symbol (without its quote) or a number as a choice, but found a string
referring to the line (5):
((string=? "a" "b")(string-append("-" (aaa(substring x 1)))))
This line was actually supposed to look like this,
((string=? (substring x 0 1) "+")(string-append("-" (aaa(substring x 1)))))
but I thought that using two strings "a" "b" would ease to spot the problem.
I don't understand why i get this error, since both "a" "b" are strings and not symbols or lists, and also I can't understand why I don't get this error on the previous line
CONTEXT:
the procedure is supposed check if the first character of a string is a +/-/., and then do thing through recursion using else (again, "a" "b" are examples)
It seems like you are mixing cond, the flat if-elseif-else, and case, which is similar to a switch statement.
How to use cond:
(cond ((equal? 1 2) 'consequent)
((odd? 1) 'consequent2) ; as many terms as you want. It stops at the first true
(else 'alternative)) ; or else it evaluates the alterntaive
; ==> consequent2
Vs how to use case
(case 'value4
((value1 value2) 'consequent)
((value3 value4) 'consequent2)
(else 'default))
; ==> consequent2
Now that case statement is just sugar. Your Scheme implementation will make it into something similar to this:
(cond ((and (eqv? 'value4 'value1) (eqv? 'value4 'value2)) 'consequent)
((and (eqv? 'value4 'value3) (eqv? 'value4 'value4)) 'consequent2)
(else 'default))
Thus notice the values to match in a case are treated as if they are quoted. Eg. you values can not be variables since they will only match their symbol.
If you want to use case I would have done this:
(define (first-letter-operator? str)
(case (string-ref str 0)
((#\+ #\- #\.) #t)
(else #f)))
(first-letter-operator? "+345634") ; ==> #t
(first-letter-operator? "hello") ; ==> #f
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.
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"
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.
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.