--- a/DOCUMENTATION.markdown Mon Jul 12 22:03:01 2021 -0400
+++ b/DOCUMENTATION.markdown Wed Nov 10 21:13:00 2021 -0500
@@ -179,6 +179,33 @@
+## Package `LOSH.BASE`
+
+A few utilities re-exported from Alexandria, plus some other basic stuff.
+
+### `MKSTR` (function)
+
+ (MKSTR &REST ARGS)
+
+### `SYMB` (function)
+
+ (SYMB &REST ARGS)
+
+### `TIMING` (macro)
+
+ (TIMING (&KEY (TIME :RUN) (RESULT-TYPE 'INTEGER))
+ &BODY
+ BODY)
+
+Execute `body`, discard its result, and return the time taken.
+
+ `time` must be one of `:run` or `:real`.
+
+ `result-type` must be `integer` (which will return internal time units) or
+ `rational`/`single-float`/`double-float` (which will return seconds).
+
+
+
## Package `LOSH.BITS`
Utilities for low-level bit stuff.
@@ -1181,7 +1208,7 @@
### `PLOT` (function)
- (PLOT DATA &KEY (STYLE :LINESPOINTS) (FILE plot.pdf))
+ (PLOT DATA &KEY (STYLE :LINESPOINTS) (FILE plot.pdf) (LOGSCALE NIL))
Plot `data` with gnuplot.
@@ -2596,8 +2623,9 @@
completes. If false, it will return immediately and allow the program to run
asynchronously.
- `input` must be a character input stream, a string, or `nil`. If non-`nil`
- its contents will be sent to the program as its standard input.
+ `input` must be a character input stream, a string, a list of strings, or
+ `nil`. If non-`nil` its contents will be sent to the program as its standard
+ input. A list of strings will be sent separated by newlines.
`result-type` must be one of:
--- a/make-docs.lisp Mon Jul 12 22:03:01 2021 -0400
+++ b/make-docs.lisp Wed Nov 10 21:13:00 2021 -0500
@@ -4,6 +4,7 @@
(list "LOSH"
"LOSH.ARRAYS"
+ "LOSH.BASE"
"LOSH.BITS"
"LOSH.CHILI-DOGS"
"LOSH.CLOS"
--- a/src/debugging.lisp Mon Jul 12 22:03:01 2021 -0400
+++ b/src/debugging.lisp Wed Nov 10 21:13:00 2021 -0500
@@ -210,6 +210,31 @@
(stop-profiling))))
+(defmacro timing ((&key (time :run) (result-type 'integer)) &body body)
+ "Execute `body`, discard its result, and return the time taken.
+
+ `time` must be one of `:run` or `:real`.
+
+ `result-type` must be `integer` (which will return internal time units) or
+ `rational`/`single-float`/`double-float` (which will return seconds).
+
+ "
+ (with-gensyms (start end result)
+ `(let ((,start ,(ecase time
+ (:run '(get-internal-run-time))
+ (:real '(get-internal-real-time)))))
+ (progn ,@body)
+ (let* ((,end ,(ecase time
+ (:run '(get-internal-run-time))
+ (:real '(get-internal-real-time))))
+ (,result (- ,end ,start)))
+ ,(ecase result-type
+ (integer `,result)
+ (rational `(/ ,result internal-time-units-per-second))
+ (single-float `(coerce (/ ,result internal-time-units-per-second) 'single-float))
+ (double-float `(coerce (/ ,result internal-time-units-per-second) 'double-float)))))))
+
+
(defmacro gimme (n &body body)
`(iterate (repeat ,n)
(collect (progn ,@body))))
--- a/src/gnuplot.lisp Mon Jul 12 22:03:01 2021 -0400
+++ b/src/gnuplot.lisp Wed Nov 10 21:13:00 2021 -0500
@@ -105,7 +105,7 @@
;;;; Convenience Wrappers -----------------------------------------------------
-(defun plot (data &key (style :linespoints) (file "plot.pdf"))
+(defun plot (data &key (style :linespoints) (file "plot.pdf") (logscale nil))
"Plot `data` with gnuplot.
Convenience wrapper around the gnuplot functions. This is only intended for
@@ -116,8 +116,8 @@
(with-gnuplot ()
(gnuplot-data "$data" data)
(gnuplot-format "set terminal pdfcairo size 10in, 8in
- set output '~A'
- plot $data using 1:2 with ~A"
- file
- (string-downcase (symbol-name style)))))
-
+ set output '~A'"
+ file)
+ (when logscale
+ (gnuplot-format "set logscale y"))
+ (gnuplot-format "plot $data using 1:2 with ~A" (string-downcase (symbol-name style)))))
--- a/src/iterate.lisp Mon Jul 12 22:03:01 2021 -0400
+++ b/src/iterate.lisp Wed Nov 10 21:13:00 2021 -0500
@@ -1112,3 +1112,77 @@
"Sugar for `(if-first-iteration nil expr)`."
`(if-first-iteration nil ,expr))
+
+(defmacro-clause (FOR vars WINDOW size ON sequence-or-length
+ &optional START start END end)
+ "Iterate a window over a sequence.
+
+ The exact nature of the iteration depends on the form of `vars`:
+
+ If `vars` is a symbol, or a list of a single symbol, it will be bound to
+ a `size`-element `subseq` of `sequence-or-length` on each iteration. In this
+ case, `sequence-or-length` must be a sequence.
+
+ If `vars` is a list of two symbols `(start end)`, they will be bound to the
+ start and end bounding indices of a a `size`-element window of
+ `sequence-or-length` on each iteration. In this case, `sequence-or-length`
+ can be a sequence (in which case its `length` is used) or an integer.
+
+ If `vars` is a list of three symbols `(subseq start end)`, both of the above
+ bindings will happen. In this case, `sequence-or-length` must be a sequence.
+
+ If `start` or `end` are given, they are used to restrict the range of the
+ sequence being iterated over.
+
+ `generate` is not supported at this time.
+
+ Examples:
+
+ (iterate (with string = \"abcdefg\")
+ (for x :window 2 :on string)
+ (collect x))
+ ; => (\"ab\" \"bc\" \"cd\" \"de\" \"ef\" \"fg\")
+
+ (iterate (with string = \"abcdefg\")
+ (for (start end) :window 2 :on 5)
+ (collect (subseq string start end)))
+ ; => (\"ab\" \"bc\" \"cd\" \"de\")
+
+ (iterate (with string = \"abcdefg\")
+ (for (x start end) :window 2 :on string)
+ (collect (list x start end)))
+ ; => ((\"ab\" 0 2) (\"bc\" 1 3) (\"cd\" 2 4) (\"de\" 3 5) (\"ef\" 4 6) (\"fg\" 5 7))
+
+ (iterate (with string = \"abcdefg\")
+ (for (x start end) :window 2 :on string :start 1 :end 5)
+ (collect (list x start end)))
+ ; => ((\"bc\" 1 3) (\"cd\" 2 4) (\"de\" 3 5))
+
+ "
+ (setf vars (ensure-list vars))
+ (alexandria:with-gensyms (n seq s)
+ (let (subseq% start% end%)
+ (ecase (length vars)
+ (1 (setf subseq% (first vars)
+ start% (gensym "START")
+ end% (gensym "END")))
+ (2 (setf subseq% nil
+ start% (first vars)
+ end% (second vars)))
+ (3 (setf subseq% (first vars)
+ start% (second vars)
+ end% (third vars))))
+ `(progn
+ (with ,s = ,(or start 0))
+ (with ,n = ,size)
+ (with ,seq = ,sequence-or-length)
+ (for ,start% :from ,s)
+ (for ,end% :from (+ ,n ,s) :to
+ ,(cond
+ (end end)
+ (subseq% `(length ,seq))
+ (t `(etypecase ,seq
+ (integer ,seq)
+ (sequence (length ,seq))))))
+ ,@(when subseq%
+ `((for ,subseq% = (subseq ,seq ,start% ,end%))))))))
--- a/src/package.lisp Mon Jul 12 22:03:01 2021 -0400
+++ b/src/package.lisp Wed Nov 10 21:13:00 2021 -0500
@@ -19,6 +19,7 @@
(defpackage :losh.base
(:use :cl)
+ (:documentation "A few utilities re-exported from Alexandria, plus some other basic stuff.")
(:import-from :alexandria
:compose :curry :rcurry
:with-gensyms :once-only
@@ -29,6 +30,8 @@
:with-gensyms :once-only
:ensure-list
+ :timing ; both profiling and iterate use this symbol
+
:symb :mkstr))
@@ -346,6 +349,7 @@
:unless-first-time
:when-first-iteration
:when-first-time
+ :window
:within-radius
))
@@ -418,6 +422,7 @@
:prl
:shut-up
:structural-string
+ :timing
))
@@ -453,6 +458,7 @@
(defpackage-inheriting :losh
(
+ :losh.base
:losh.arrays
:losh.bits
:losh.chili-dogs
--- a/src/shell.lisp Mon Jul 12 22:03:01 2021 -0400
+++ b/src/shell.lisp Wed Nov 10 21:13:00 2021 -0500
@@ -10,8 +10,9 @@
completes. If false, it will return immediately and allow the program to run
asynchronously.
- `input` must be a character input stream, a string, or `nil`. If non-`nil`
- its contents will be sent to the program as its standard input.
+ `input` must be a character input stream, a string, a list of strings, or
+ `nil`. If non-`nil` its contents will be sent to the program as its standard
+ input. A list of strings will be sent separated by newlines.
`result-type` must be one of:
@@ -31,6 +32,7 @@
(ctypecase input
(string (setf input (make-string-input-stream input)))
(vector (setf input (flexi-streams:make-in-memory-input-stream input)))
+ (cons (setf input (make-string-input-stream (format nil "~{~A~^~%~}" input)))) ; todo make this not cons as much
(stream)
(null))
(when (not wait)