Hello! Efraim Flashner <efr...@flashner.co.il> skribis:
> GSoC just started and so far I have 13 tabs from the guile manual open > on my computer. I almost implemented `du' at the same time as `wc' but > at the time I was getting tripped up with functions returning functions > or returning values. Nice! Some comments to complement what Ricardo et al. wrote: > +(define (wc-w-command file) > + (let* ((input-file (open-file file "r")) > + (line (make-string 80)) > + (word-size (read-delimited! " \t\n" line input-file)) > + (word-count 0)) > + (while (not (port-closed? input-file)) > + (if (not (zero? word-size)) > + (set! word-count (1+ word-count))) > + (set! line (make-string 80)) > + (if (not (eof-object? (peek-char input-file))) > + (set! word-size (read-delimited! " \t\n" line input-file)) > + (close-input-port input-file))) > + word-count)) First, regarding the port not being closed, it’s a good idea to use ‘call-with-input-file’ instead of ‘open-file’: ‘call-with-input-file’ makes sure the port gets closed when its “dynamic extent” is left, that is, regardless of whether we leave the procedure “normally” or via an exception. (call-with-input-file file (lambda (port) …)) Also, imperative programming Should Be Avoided; see “Coding Style” in the manual. ;-) We don’t use an i/o monad or any such thing, but ‘set!’ and string modifications is definitely frowned upon. As Ricardo suggests, you could use ‘port->stream’ and ‘stream-fold’ to iterate over the characters read from the port. I suspect that’d be rather slow though, at least on 2.0, so another option is something like: (define (lines+chars port) ;; Return the number of lines and number of chars read from PORT. (let loop ((lines 1) (chars 0)) (match (read-char port) ((? eof-object?) ;done! (values lines port)) (#\newline ;recurse (loop (+ 1 lines) (+ 1 chars))) (_ ;recurse (loop lines (+ 1 chars)))))) (define (wc-command file) (let-values (((lines chars) (call-with-input-file file lines+chars))) (format #t "~a ~a ~a~%" lines chars file))) > +(define (wc-command file) > + (if (and (file-exists? file) (access? file 4)) This check is not needed and is subject to a race condition (“TOCTTOU”); just let ‘call-with-input-file’ error out if the file cannot be read. Bonus point: catch ‘system-error’ exceptions and report the inability to open the file in a nice user-friendly way (but really, don’t bother about it for now.) > + (let* ((wc-l ((@@ (guix build bournish) wc-l-command) file)) > + (wc-w ((@@ (guix build bournish) wc-w-command) file)) > + (wc-c ((@@ (guix build bournish) wc-c-command) file))) > + (begin > + (display wc-l)(display #\space) > + (display wc-w)(display #\space) > + (display wc-c)(display #\space) > + (display file) > + (newline))))) Remember that Bournish is a compiler that compiles Bash to Scheme. So we must distinguish the support functions that are used at run time, such as ‘ls-command-implementation’, from what the Scheme code that the compiler emits (compile time). In the case of ‘ls’, when the compiler encounters ‘ls’ in the input, it emits this code: ((@@ (guix build bournish) ls-command-implementation)) ‘ls-command-implementation’ is the implementation that is called when we run the compiled program. Thus, you must similarly distinguish those two stages by providing: 1. A ‘wc-command-implementation’ procedure that implements ‘wc’; 2. A ‘wc-command’ procedure that emits the code that calls ‘wc-command-implementation’; so something like: (define (wc-command args) `((@@ (guix build bournish) wc-command-implementation) ,@args)) Better yet, ‘wc-command’ could check for the presence of “-l” or “-c” at compile time and emit a call to the right thing. HTH! Ludo’.