Scheme Help - File Statistics - file-io

So I have to finish a project in Scheme and I'm pretty stuck. Basically, what the program does is open a file and output the statistics. Right now I am able to count the number of characters, but I also need to count the number of lines and words. I'm just trying to tackle this situation for now but eventually I also have to take in two files - the first being a text file, like a book. The second will be a list of words, I have to count how many times those words appear in the first file. Obviously I'll have to work with lists but I would love some help on where to being. Here is the code that I have so far (and works)
(define filestats
(lambda (srcf wordcount linecount charcount )
(if (eof-object? (peek-char srcf ) )
(begin
(close-port srcf)
(display linecount)
(display " ")
(display wordcount)
(display " ")
(display charcount)
(newline) ()
)
(begin
(read-char srcf)
(filestats srcf 0 0 (+ charcount 1))
)
)
)
)
(define filestatistics
(lambda (src)
(let ((file (open-input-file src)))
(filestats file 0 0 0)
)
)
)

How about 'tokenizing' the file into a list of lines, where a line is a list of words, and a word is a list of characters.
(define (tokenize file)
(with-input-from-file file
(lambda ()
(let reading ((lines '()) (words '()) (chars '()))
(let ((char (read-char)))
(if (eof-object? char)
(reverse lines)
(case char
((#\newline) (reading (cons (reverse (cons (reverse chars) words)) lines) '() '()))
((#\space) (reading lines (cons (reverse chars) words) '()))
(else (reading lines words (cons char chars))))))))))
once you've done this, the rest is trivial.
> (tokenize "foo.data")
(((#\a #\b #\c) (#\d #\e #\f))
((#\1 #\2 #\3) (#\x #\y #\z)))

The word count algorithm using Scheme has been explained before in Stack Overflow, for example in here (scroll up to the top of the page to see an equivalent program in C):
(define (word-count input-port)
(let loop ((c (read-char input-port))
(nl 0)
(nw 0)
(nc 0)
(state 'out))
(cond ((eof-object? c)
(printf "nl: ~s, nw: ~s, nc: ~s\n" nl nw nc))
((char=? c #\newline)
(loop (read-char input-port) (add1 nl) nw (add1 nc) 'out))
((char-whitespace? c)
(loop (read-char input-port) nl nw (add1 nc) 'out))
((eq? state 'out)
(loop (read-char input-port) nl (add1 nw) (add1 nc) 'in))
(else
(loop (read-char input-port) nl nw (add1 nc) state)))))
The procedure receives an input port as a parameter, so it's possible to apply it to, say, a file. Notice that for counting words and lines you'll need to test if the current char is either a new line character or a white space character. And an extra flag (called state in the code) is needed for keeping track of the start/end of a new word.

Related

scheme: resuming a loop after a condition has been signaled

This program is using Scheme conditions and restarts to execute a procedure 10 times in a loop, and return the number of times the procedure did succeed.
Here the procedure throws an error each times n is a multiple of 3.
For some reason, the first error (n=3) is caught but the loop fails to resume with n=4:
(define (handle! thunk)
(bind-condition-handler
'() (lambda (condition)
(display "resuming...")
(invoke-restart (find-restart 'next)))
thunk))
(let loop((n 1) (count 0))
(display n)(display #\,)
(if (> n 10) count
(handle!
(call/cc
(lambda (cc)
(with-restart
'next "restart here"
(lambda ()
(cc (loop (1+ n) count)))
#f
(lambda ()
(if (= 0 (modulo n 3))
(error n "is multiple of 3!")
(loop (1+ n) (1+ count))))))))))
I failed to find examples of conditions and restarts beyond the ones at the MIT Scheme Reference.
The solution is to move down the call/cc to the loop argument 'count' which is affected by the condition:
(let loop((n 1) (count 0))
(display n)(display #\,)
(if (> n 10) count
(handle!
(lambda()
(loop (1+ n)
(call/cc
(lambda (cc)
(with-restart
'next "restart here"
(lambda ()
(cc count))
#f
(lambda ()
(if (= 0 (modulo n 3))
(error n "is multiple of 3!"))
(1+ count))))))))))
Runs correctly:
1 ]=> 1,2,3,resuming...4,5,6,resuming...7,8,9,resuming...10,11,
;Value: 7

How to read a line of input in Chez-Scheme?

I can't find out how to do this. In previous implementations read-line was available but for some reason it isn't in Chez.
How do I just read a line of input?
Chez Scheme is the R6RS implementation.
Use the R6RS get-line instead of the R7RS read-line.
I have a read-line in my Standard Prelude; it handles end-of-line as carriage-return, line-feed, or both in either order:
(define (read-line . port)
(define (eat p c)
(if (and (not (eof-object? (peek-char p)))
(char=? (peek-char p) c))
(read-char p)))
(let ((p (if (null? port) (current-input-port) (car port))))
(let loop ((c (read-char p)) (line '()))
(cond ((eof-object? c) (if (null? line) c (list->string (reverse line))))
((char=? #\newline c) (eat p #\return) (list->string (reverse line)))
((char=? #\return c) (eat p #\newline) (list->string (reverse line)))
(else (loop (read-char p) (cons c line)))))))

Clojure optimization of an inversion counter

I'm new to Clojure. I was wondering how I could optimize an algorithm to count the number of inversions in a list. From what I understand, Clojure doesn't do tail call optimization unless specifically asked to? How do you get it to do this?
This first attempt with a mutated variable has a runtime of about 3.5s. But my second attempt was a functional version and it takes about 1m15s! and both require growing the stack size quite a bit (like -Xss12m).
How would I go about getting better performance?
I'd prefer to not have mutable variables (like the functional one) if possible. You can create the array file by typing something like seq 100000 | sort -R > IntArray.txt.
The first attempt w/ mutable variable:
(use 'clojure.java.io)
(def inversions 0)
(defn merge_and_count' [left right left_len]
(if (empty? right) left
(if (empty? left) right
(if (<= (first left) (first right))
(cons (first left) (merge_and_count' (rest left) right (- left_len 1)))
(let [_ (def inversions (+ inversions left_len))]
(cons (first right) (merge_and_count' left (rest right) left_len)))
))))
(defn inversion_count [list]
(if (or (empty? list) (nil? (next list))) list
(let [mid (quot (count list) 2)]
(merge_and_count' (inversion_count (take mid list))
(inversion_count (drop mid list)) mid)
)))
(defn parse-int [s]
(Integer. (re-find #"\d+" s )))
(defn get-lines [fname]
(with-open [r (reader fname)]
(doall (map parse-int (line-seq r)))))
(let [list (get-lines "IntArray.txt")
_ (inversion_count list)]
(print inversions))
My second attempt to be purely functional (no mutability):
(use 'clojure.java.io)
(defn merge_and_count' [left right inversions]
(if (empty? right) (list left inversions)
(if (empty? left) (list right inversions)
(if (<= (first left) (first right))
(let [result (merge_and_count' (rest left) right inversions)]
(list (cons (first left) (first result)) (second result)))
(let [result (merge_and_count' left (rest right) (+ inversions (count left)))]
(list (cons (first right) (first result)) (second result)))
))))
(defn inversion_count [list' list_len]
(if (or (empty? list') (nil? (next list'))) (list list' 0)
(let [mid (quot list_len 2)
left (inversion_count (take mid list') mid)
right (inversion_count (drop mid list') (- list_len mid))]
(merge_and_count' (first left) (first right) (+ (second left) (second right)))
)))
(defn parse-int [s]
(Integer. (re-find #"\d+" s )))
(defn get-lines [fname]
(with-open [r (reader fname)]
(doall (map parse-int (line-seq r)))))
(let [list (get-lines "IntArray.txt")
result (inversion_count list 100000)]
(print (second result)))
The stack overflows due to the recursion in merge-and-count. I tried this approach, and for 100000 items, it came back instantly.
(defn merge_and_count [left right inversions]
(loop [l left r right inv inversions result []]
(cond (and (empty? r) (empty? l)) [result inv]
(empty? r) [(apply conj result l) inv]
(empty? l) [(apply conj result r) inv]
(<= (first l) (first r)) (recur (rest l) r inv (conj result (first l)))
:else (recur l (rest r) (+ inv (count l)) (conj result (first r))))))
You need to replace this code with code from your second approach.

application: not a procedure racket

i am new to racket. i am trying create a list from the input of the user and when the value 0 is entred the first three elements are printed.
here is the code:
#lang racket
(define lst '())
(define (add)
(define n(read))
(if (= n 0)
;then
(
list (car lst) (cadr lst) (caddr lst)
)
;else
(
(set! lst (append lst (list n)))
(add)
)
)
)
(add)
i tested the program with the values 1 2 3 4 5 0
but i keep getting this error:
application: not a procedure;
expected a procedure that can be applied to arguments
given: #<void>
arguments...:
'(1 2 3)
can anyone help me figure out what's wrong.
If you have more than one expression in the "then" or "else" parts, you must enclose them inside a begin, because a pair of () in Scheme are used for function application - that explains the error you're getting. Try this:
(define (add)
(define n (read))
(if (= n 0)
; then
(list (car lst) (cadr lst) (caddr lst))
; else
(begin
(set! lst (append lst (list n)))
(add))))
I had a similar problem, in a function i called a parameter with the same name of a structure, so, trying to create an instance of that structure i got the same error.
example:
> (struct example (param1 param2) #:transparent)
> (define e (example 1 2))
> e
(example 1 2)
> (define (fn e)
(example (example-param1 e) 0))
> (fn e)
(example 1 0)
> (define (fn example)
(example (example-param1 example) 0))
> (fn e)
application: not a procedure;
expected a procedure that can be applied to arguments
given: (example 1 2)
arguments...:
I hope this helps
Your code have a few problems, for example it will fail if you enter less than 3 elements. Also, it is not considered good style to define variables at the module level.
I'd suggest the following:
(define (add)
(define (sub cnt lst)
(define n (read))
(if (= n 0)
(reverse lst)
(if (< cnt 3)
(sub (add1 cnt) (cons n lst))
(sub cnt lst))))
(sub 0 '()))

GIMP:Script-Fu-script error

I try batch-processing some files with GIMP.
Here is the script:
(define (script-fu-batch-sofu globexp)
(define (sofu-trans-img n f)
(let* ((fname (car f))
(img (car (gimp-file-load 1 fname fname))))
(gimp-image-undo-disable img)
(gimp-fuzzy-select (car (gimp-image-get-active-drawable img)) 0 0 10 2 FALSE FALSE 0 FALSE)
(gimp-selection-grow img 1)
(gimp-edit-clear (car (gimp-image-get-active-drawable img)))
(file-png-save-defaults non-interactive img (car (gimp-image-get-active-drawable img)) fname fname)
(gimp-image-delete img)
)
(if (= n 1) 1 (sofu-trans-img (- n 1) (cdr f)))
)
(set! files (file-glob globexp 0))
(sofu-trans-img (car files) (car (cdr files)))
)
; Im GIMP und im Menü registrieren
(script-fu-register "script-fu-batch-sofu"
_"_Mehrere Bilder transparentieren…"
_"Mehrere Bilder auf einmal transparent machen"
"Martin Weber"
"2012, Martin Weber"
"Sep 5, 2012"
""
SF-STRING "Zu transparentierende Dateien" "/pfad/zu/bildern/*.bmp")
(script-fu-menu-register "script-fu-batch-sofu" "<Image>/Xtns/Misc")
I basically copied a script from a tutorial and modified the lines where the image is processed.
If i run the script, i get this error message:
Error: set!: unbound variable: files
To be honest, i don't really know what that part does, but i think i need it.
I guess it opens the Files given by the script-parameter and precesses them one after the other.
I just don't know what is wrong there. What did i miss?
I think that you need to add
(define files)
at the first line of your code.