Added map-lines(). As well as forwarding helper functions and macros map-file-lines(), and map{can,con}[-file]-lines().

TODO: They are still untested.

Fortune for cl-utils's current commit: Future small blessing − 末小吉
master
Avril 3 years ago
parent de21818183
commit b0c7e4bc4a
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -120,6 +120,64 @@
(loop for line = (funcall read-line input nil)
while line do (funcall fi line)))
(defun map-lines (stream func &key
(ignore nil)
(applicator #'list)
(mapper #'mapcar)
(transform #'identity)
(read-line (lambda (stream) (read-line stream nil)))
(continue #'identity))
"Maps over the lines in stream `stream', applying `applicator' to the result of `mapper' being called with `func' when called with the lines in-order.
To transform the line before processing, you can set the `transform' argument: This will be passed the raw input line, and the line used for the rest of the mapping is the result of that function. (If `transform' returns ``nil'', the line is treated as blank, otherwise, it must return a string.)
To ommit a certain kind of line from being sent to the mapping function, you can set `ignore' to:
- `:blank' Ignore blank lines
- `:whitespace-only' Ignore lines that contain just whitespaces
- Any functor that will take the line as an argument. If the call returns a non-truthy value, the line is ignored.
To use a custom line reader function, set `read-line' to a function that takes a stream and returns a string (or ``nil'', on EOF).
To stop on a specific line, `continue' can be set to a function that receives the line string; and if `nil' is returned from that function, the iteration stops.
The default behaviour (with `mapper' being `mapcar' and `applicator' being `list') works just like `mapcar'(). To emulate the behaviour of functions like `mapcan'(), set `applicator' to `nconc'(); then set `mapper' to `maplist'() for `mapcon'() behaviour."
;(with-open-file (stream location :direction :input)
(let ((filter-single-line (switch ignore
;; Specific `ignore' values
(:blank (lambda (line) (> (length line) 0)))
(:whitespace-only (lambda (line) (not (cl-ppcre:scan "^\s*$" line))))
;; Otherwise, the ignore function
ignore)))
(apply applicator ; apply `applicator' to the result of each iteration in `mapper'.
(funcall mapper func ; call the mapping function with `func' and the list of transformed and filtered lines
(mapcan (lambda (n) (when n (list n))) ; outputs a list of the lines
(loop for line = (funcall read-line stream)
while (and line (funcall continue line))
collect (let ((line (funcall transform line)))
(when (funcall filter-single-line line) line))))))))
(defmacro map-file-lines (location func &rest kvs &key &allow-other-keys)
"See `map-lines'(): Maps `func' over a file `location' instead of a stream."
(let ((stream (gensym)))
`(with-open-file (,stream ,location :direction :input)
,(cons 'map-lines (append `(,stream ,func) kvs)))))
;TODO: test map-lines -- (map-file-lines "file" #'identity :applicator #'nconc :ignore :blank)
(defun mapcan-lines (stream func &rest kvs &key &allow-other-keys)
"See `map-lines'(): Uses `nconc'() as the applicator and `mapcar'() as the mapper, which produces an output you'd expect from `mapcan'() The other key arguments can be specified according to the signature of `map-lines'()."
(apply #'map-lines (append (list stream func :applicator #'nconc :mapper #'mapcar) kvs)))
(defmacro mapcan-file-lines (location func &rest kvs &key &allow-other-keys)
"See `mapcan-lines'(): Maps `func' over a file `location' instead of a stream."
(let ((stream (gensym)))
`(with-open-file (,stream ,location :direction :input)
,(cons 'mapcan-lines (append `(,stream ,func) kvs)))))
(defun mapcon-lines (stream func &rest kvs &key &allow-other-keys)
"See `map-lines'(): Uses `nconc'() as the applicator and `maplist'() as the mapper, which produces an output you'd expect from `mapcon'(). The other key arguments can be specified according to the signature of `map-lines'()."
(apply #'map-lines (append (list stream func :applicator #'nconc :mapper #'maplist) kvs)))
(defmacro mapcon-file-lines (location func &rest kvs &key &allow-other-keys)
"See `mapcon-lines'(): Maps `func' over a file `location' instead of a stream."
(let ((stream (gensym)))
`(with-open-file (,stream ,location :direction :input)
,(cons 'mapcon-lines (append `(,stream ,func) kvs)))))
(defun strcat (&rest str)
"Concat all strings, if item is not string it is written to one."
(apply #'concatenate (cons 'string (mapcar #'(lambda (x)

Loading…
Cancel
Save