---
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/)