--- layout: ../Site.layout.js --- # Line plotting Alexandria's forms' atom heights (!) (Just look.) Look at the one at the end. In the article I just wrote musing about headings, I was gnuplot line plotting lisp forms. Here I am doing one for Common Lisp's [Alexandria](https://alexandria.common-lisp.dev/). Alexandria is a special package which it is assumed you have always loaded for the most part. For example, the ANSI CL standard doesn't include `flatten` or association-list-to-hash-table, but Alexandria does. Let's do one with every form from `"~/common-lisp/alexandria/alexandria-1/hash-tables.lisp"`. Er, I'm going to use [cl-series](https://gitlab.common-lisp.net/rtoy/cl-series/\-/wikis/Series-User's-Guide). ## `defun` a call to gnuplot [From here again](/programming/screwlisps-knowledge-simple-gnuplot/). It's going to need to be modified to be noninteractive, but interactive is fine for now. ``` (defun gnuplot (title &rest x-y-lists) (let ((cmd (format nil "gnuplot -p -e \"~ set title '~a'; ~ unset key; ~ unset xtics; ~ set xrange [~,1f:~,1f]; ~ set yrange [~,1f:~,1f]; ~ plot ~{~1*'-' using 1:2 with lines~^,~^ ~};~ \"" title (apply 'min (mapcar 'car (apply 'append x-y-lists))) (apply 'max (mapcar 'car (apply 'append x-y-lists))) (apply 'min (mapcar 'cadr (apply 'append x-y-lists))) (apply 'max (mapcar 'cadr (apply 'append x-y-lists))) x-y-lists))) (with-input-from-string (in (format nil "~{~{~{~,1f ~,1f~}~%~}e~^~%~}" x-y-lists)) (uiop:run-program cmd :input in)))) ``` ## Turn a lisp form into a atom-depth list [From here](/programming/the-heading-problem/), a moment ago ``` (defun atom-heights (form &optional (height 0)) (loop :for item :in form :if (atom item) :collect height :into heights :else :nconc (atom-heights item (1+ height)) :into heights :if (atom item) :collect item :into items :else :nconc (nth-value 1 (atom-heights item)) :into items :finally (return (values heights items)))) ``` ## Install cl-series ```  (setq eepitch-buffer-name "*slime-repl ECL*") (require :series) (series::install) ``` ## Get all the lisp files in a directory ## name wild type lisp wild inferiors current directory ``` (let* ((current-dir (car (directory #p"./"))) (wild-dir (make-pathname :directory '(:relative :wild-inferiors)) ) (wild-here (merge-pathnames wild-dir current-dir)) (lisp-files (make-pathname :name :wild :type "lisp")) (lisp-here (merge-pathnames wild-here lisp-files))) lisp-here) ``` ## Get those. ``` (directory *) ``` ## Graph one of those. ``` (second *) (scan-file *) (collect *) (third *) (nth-value 0 (atom-heights *)) ``` ## Plot that like before. ``` (let* ((ys (mapcar 'list *)) (xs (loop :for x from 0 :below (length ys) :collect x))) (gnuplot "sequence-of-length-p from alexandria" (pairlis xs ys))) ``` Neat. This implies ## Plot the whole file, hoping not to run out of heap ``` (defun plot-file-heights (file) (let* ((z (scan-file file)) (exprs (collect z))) (apply 'gnuplot (pathname-name file) (loop :for expr :in exprs :for heights := (atom-heights expr) :for ys := (mapcar 'list heights) :for xs := (loop :for x :below (length ys) :collect x) :collect (mapcar 'cons xs ys))))) ``` ## `#p"alexandria-1/sequences.lisp"` ``` (plot-file-heights #P"~/common-lisp/alexandria/alexandria-1/sequences.lisp") ``` # Conclusions I have to say, that was deeply satisfying. # Fin. 1. Show me you doing ⬆ or seek help 2. Discuss what information is visible ?? [on the Mastodon thread](https://gamerplus.org/@screwlisp/115167395892550600). # Bonus [`#p"marklib.lisp"`](https://gitlab.com/mdhughes/arrokoth/-/blob/main/marklib.lisp) from [Arrokoth](https://mdhughes.tech/software/arrokoth/)