---
layout: ../Site.layout.js
---
# Line plotting Alexandria's forms' atom heights (!) (Just look.)

<img src="../alexandria-hash-tables.png">

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

<img src="../alexandria-sequence-of.png">

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")
```

<img src="../alexandria-sequences.png">

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

<img src=../marklib-heights.png>