My main method (remove-random-edge) looks quite difficult to read. I'm new to list, so would appreciate any advice on how to improve the code.
(defun find-node (node graph)
(find-if #'(lambda (i) (eql node (first i))) graph))
;; Input, graph, is in form ((from-1 to-1 to-2 to-3 ...)
;; (from-2 to-4 to-5 to-6 ...) ...)
;; where from-n and to-n are integers.
(defun remove-random-edge (graph)
"Remove one random edge from a graph given as adjacency list."
(let* ((node-list-1 (elt graph (random (length graph))))
(node-1 (first node-list-1))
(destinations (remove-duplicates (rest node-list-1)))
(node-2 (elt destinations (random (length destinations))))
(node-list-2 (find-node node-2 graph)))
(flet ((replace-tail-for-head (node) (if (eql node node-2) node-1 node))
(is-head-p (node) (eql node-1 node))
(is-tail-p (node) (eql node-2 node))
(starts-with-tail-p (nodes) (eql node-2 (first nodes))))
(setf (rest node-list-1) (concatenate 'list
(rest node-list-1)
(remove-if #'is-head-p (rest node-list-2))))
(loop for node in (remove-duplicates (rest node-list-2))
with match
with repcd
do (setf match (find-node node graph))
do (setf repcd (if (eql node node-1)
(remove-if #'is-tail-p (rest match))
(map 'list #'replace-tail-for-head (rest match))))
do (setf (rest match) (sort repcd #'<)))
(remove-if #'starts-with-tail-p graph))))
UPD: With review comments applied:
(defun remove-random-edge (graph)
"Remove one random edge from a graph given as adjacency list."
(let* ((head-list (elt graph (random (length graph))))
(head (first head-list))
(destinations (remove-duplicates (rest head-list)))
(tail (elt destinations (random (length destinations))))
(tail-list (assoc tail graph)))
(flet ((replace-tail-for-head (node) (if (eql node tail) head node)))
(setf (rest head-list) (concatenate 'list
(rest head-list)
(remove head (rest tail-list))))
(loop for node in (remove-duplicates (rest tail-list))
for match = (assoc node graph)
do (setf (rest match) (if (eql node head)
(remove tail (rest match))
(mapcar #'replace-tail-for-head (rest match)))))
(remove tail graph :key #'first))))
Before:
15.109 seconds of real time
50,245,719,578 processor cycles
767,039,256 bytes consed
After:
2.312 seconds of real time
7,665,669,728 processor cycles
778,172,816 bytes consed