William James
7/25/2015 11:39:00 PM
Erann Gat wrote:
> The recent thread on translating strings made me give some though to the
> Right Way to do these things, and I've decided that the Right Way is to
> generalize the functionality of the MAP function so that it works for
> streams as well as sequences, e.g.:
>
> (defmethod gmap (type f (s sequence)) (map type f s))
>
> (defmethod gmap (type f (s stream))
> (map type f (read-all-characters-from-stream s)))
>
> Of course, you'd want a more efficient version that didn't actually read
> the entire contents of the stream into memory, e.g.:
>
> (defun stream-char-generator (s)
> (fn () (or (read-char s nil nil) (values nil t))))
>
> (defun walk-generator (gen f)
> (loop
> (receive (elt done?) (funcall gen)
> (if done? (return nil))
> (funcall f elt))))
>
> (defmethod gmap (type f (s stream))
> (gmap type f (stream-char-generator s)))
>
> (defmethod gmap (type f (g function))
> (cond
> ( (null type) (walk-generator g f) )
> ( (subtypep type 'list)
> (with-collector collect
> (walk-generator g (fn (c) (collect (funcall f c))))) )
> ( (subtypep type 'string)
> (with-output-to-string (out)
> (walk-generator g (fn (c) (princ (funcall f c) out)))) )
> ( (subtypep type 'vector)
> (let ( (v (make-array 0 :adjustable t :fill-pointer t)) )
> (walk-generator g (fn (c) (vector-push-extend c v)))
> v) )
> (t (error "~S is not a valid output specifier for ->" type))))
>
> Now you can do:
>
> ? (gmap nil 'print "foo")
>
> #\f
> #\o
> #\o
> NIL
Gauche Scheme:
(use gauche.sequence)
(for-each print "foo")
f
o
o
#t
> ? (gmap nil 'print (make-string-input-stream "foo"))
>
> #\f
> #\o
> #\o
> NIL
> ?
(with-input-from-string "foo" (cut generator-for-each print read-char))
===>
f
o
o
#<undef>
>
> You can even walk over stream entities other than characters:
>
> (defun stream-line-generator (s)
> (fn () (or (read-line s nil nil) (values nil t))))
>
> ? (gmap 'list 'identity
> (stream-line-generator
> (make-string-input-stream "foo
> baz
> bar")))
> ("foo" "baz" "bar")
(with-input-from-string "foo
bar
baz"
(cut generator-map values read-line))
===>
("foo" "bar" "baz")
>
> And all this in under thirty minutes. Damn, Lisp is cool!
>
> E.
>
> P.S. To run this code you'll also need:
>
> (defmacro fn (args &body body) `(lambda ,args ,@body))
--
[Jesse Jackson] would spit into the food of white patrons he hated and then
smilingly serve it to them. He did this, he said, "because it gave me
psychological gratification." -- Life Magazine, 1969-11-29
Blacks are an estimated 39 times more likely to commit a violent crime
against a white than vice versa, and 136 times more likely to commit robbery.
www.colorofcrime.com/2005/10/the-color-of-crime-2005/