@ -120,6 +120,64 @@
( loop for line = ( funcall read-line input nil )
( loop for line = ( funcall read-line input nil )
while line do ( funcall fi line ) ) )
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 )
( defun strcat ( &rest str )
"Concat all strings, if item is not string it is written to one."
"Concat all strings, if item is not string it is written to one."
( apply #' concatenate ( cons 'string ( mapcar #' ( lambda ( x )
( apply #' concatenate ( cons 'string ( mapcar #' ( lambda ( x )