Grep
September 25, 2009
We write the program in stages: main
interprets command-line arguments and initializes the matcher, do-args
processes the optional filename arguments, and do-input
searches a single file. Here is the complete program:
#! /usr/bin/scheme --script
; grep [-v] [file ...]
. . . cut and paste from standard prelude and regular-expression library . . .
(define (do-input match? filename)
(do ((line (read-line) (read-line))) ((eof-object? line))
(when (match? line)
(when filename (display filename) (display ": "))
(display line) (newline))))
(define (do-args match? args)
(cond ((null? args) (do-input match? #f))
((null? (cdr args))
(with-input-from-file (car args)
(lambda () (do-input match? #f))))
(else (do ((args args (cdr args))) ((null? args))
(with-input-from-file (car args)
(lambda () (do-input match? (car args))))))))
(define (main args)
(if (string=? (car args) "-v")
(do-args
(complement (lambda (str) (rx-match? (make-rx (cadr args)) str)))
(cddr args))
(do-args (lambda (str) (rx-match? (make-rx (car args)) str)) (cdr args))))
(main (cdr (command-line)))
The header line and the command-line
function are specific to Chez Scheme, and may have to change for other Scheme systems. Main
builds a matcher on the fly, storing the compiled regular expression in its closure; the -v
option is handled by complement
, which takes a function that returns a boolean and returns a new function that returns the opposite.
You can see the program with all of its declarations at http://programmingpraxis.codepad.org/qkOnYNIC. When
, complement
and read-line
are from the Standard Prelude; the remaining functions come from the regular expression library of the previous exercises.
[…] today’s Programming Praxis exercise we’re supposed to implement grep based on the regex matching […]
My Haskell solution (see http://bonsaicode.wordpress.com/2009/09/25/programming-praxis-grep/ for a version with comments):