https://github.com/chimay/duo
Library of in place list operations in Emacs-Lisp
https://github.com/chimay/duo
elisp inplace list operation
Last synced: 12 months ago
JSON representation
Library of in place list operations in Emacs-Lisp
- Host: GitHub
- URL: https://github.com/chimay/duo
- Owner: chimay
- License: gpl-2.0
- Created: 2019-03-18T08:54:37.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2023-09-26T15:04:14.000Z (almost 3 years ago)
- Last Synced: 2024-08-07T18:31:12.400Z (almost 2 years ago)
- Topics: elisp, inplace, list, operation
- Language: Emacs Lisp
- Size: 201 KB
- Stars: 9
- Watchers: 2
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.org
- License: LICENSE
Awesome Lists containing this project
README
#+STARTUP: showall
* Table of contents :TOC_2_gh:
- [[#introduction][Introduction]]
- [[#goal][Goal]]
- [[#use-cons-as-double-pointers][Use Cons as double pointers]]
- [[#keep-it-simple][Keep It Simple]]
- [[#summary-of-available-functions][Summary of available functions]]
- [[#technical-details][Technical details]]
- [[#modify-the-argument-list][Modify the argument list]]
- [[#files][Files]]
- [[#circular-lists][Circular lists]]
- [[#next--previous-vs-after--before][Next / Previous vs After / Before]]
- [[#remove-vs-delete][Remove vs Delete]]
- [[#functions-in-arguments][Functions in arguments]]
- [[#assoc][Assoc]]
- [[#examples][Examples]]
- [[#warning][Warning]]
* Introduction
Duo is a library of in place list operations in Emacs-Lisp. Its functions modify the
original list when :
- It’s easy to get back : rotate, reverse, move, etc
- The name is clear : push, pop, add, drop, insert, remove, etc
+ When an element is removed, a reference to it is often returned
However, when it’s difficult or impossible to reverse the operation, a
new list is created, with copies of the values using =purecopy=.
For instance :
- filter
- partition
If in doubt, check their doc.
* Goal
** Use Cons as double pointers
This library is implemented with =(CAR . CDR)= =cons=, which are
called =duo=, hence the name. These cons are everywhere in Elisp,
either explicitely created or as brick components in lists. A list
variable =list= is itself the cons at the beginning of the list.
=(cdr list)= is the second cons in the list. And so one with
=(cddr list)=, =(nthcdr N list)=. Generally speaking, a member of
a list is a cons =(value . next-member-in-list)=. Most of Elisp
is built around these =(CAR . CDR)= double pointers. You can even
construct binary trees with it.
** Keep It Simple
Primarily use Elisp tools from C source code :
- Built-ins :
+ =cons=, =list=
+ =car=, =cdr=
+ =setcar=, =setcdr=
+ =nth=, =nthcdr=
+ etc
- Special forms :
+ =if=, =cond=
+ =while=
+ etc
Some functions defined in =subr.el= are also used.
* Summary of available functions
- Finding cons in list
- Finding previous / next cons
+ In plain list
+ In virtual circular list
- Replace element
- Map
- Join
- Truncate
- Next / Previous in group
- Next / Previous matching filter
- Push, Add
- Pop, Drop
- Rotate <- or ->
- Roll until a cons is first or last
- Reverse
- Insert
- Remove, Delete
+ One occurence
+ All occurence of a value
- Teleport : move after or before another cons or element
- Move previous or next
+ In plain list
+ In virtual circular list
- Exchange cons or elements
- Insert in sorted list
- Insert at group beginning or end
- Partition with a key function to form an alist
* Technical details
** Modify the argument list
When you pass a list as argument of a function, the calling scope
=list-var= holds the address of the first cons of the list. The
argument =arg-list-var= holds a copy of it. Using ~(setq list ...)~
inside the definition of the function changes the argument list
reference, not the calling scope one. So, the calling scope address is
not updated. As a result, you need either :
- to use the list symbol in argument (=*-sym-*= functions)
+ ~(function ... 'list ...)~
- to pass a reference to the list as argument (=*-ref-*= functions)
+ ~(setq reflist (cons list nil))~
+ ~(function ... reflist ...)~
- to recover the modified list as the returned value (=*-return-*= functions)
+ ~(setq list (function ... list ...))~
A common case of this situation is with functions which modify the
first cons of the list : push, pop, etc.
Check their doc to know how to recover the updated list.
** Files
- =duo-common.el= holds the functions which don’t modify the list
- =duo-symbol.el= holds the =*-sym-*= functions
- =duo-referen.el= holds the =*-ref-*= functions
- =duo-return.el= holds the =*-return-*= functions
** Circular lists
Caution : applying some of these functions to circular lists would
produce infinite loops.
However, some functions, like =*-circ-*= or =*-rotate-*=, simulate
virtual circular lists by :
- Continuing at the beginning once arrived at the end
- Continuing at the end once arrived at the beginning
** Next / Previous vs After / Before
There is a slight difference between next/previous and after/before
functions :
- Next / Previous use a cons as main argument
- After / Before use the value of an element of the list as main argument
** Remove vs Delete
There is a slight difference between remove and delete functions :
- Remove removes a cons given as argument
- Delete remove the first cons whose car matches an element given as argument
** Functions in arguments
Some functions accept a function =fn-*= in argument. Among these
=fn-*=, some takes two arguments. When this is the case, they are
called internally like this :
#+begin_src emacs-lisp
(funcall fn-* elem-or-cons-from-argument cons-from-loop)
#+end_src
** Assoc
The classic =assoc= function return the cons =(key . value)=, which is
the /content/ of the Alist element, whereas the =duo-assoc= function
return the duo =((key . value) . next-member-in-alist)=, real member of
the Alist.
* Examples
#+begin_src emacs-lisp
(require 'duo-common)
(setq mylist '(1 2 3 4 5 6 7))
(print (duo-slice mylist 3 -1))
(setq mylist '(1 2 3 4 5 6 7 1 1))
(duo-replace-all 1 2 mylist)
(print mylist)
(require 'duo-symbol)
(setq mylist '(1 2 3 4 5 6 7))
(duo-sym-rotate-left 'mylist)
(print mylist)
(duo-sym-rotate-right 'mylist)
(print mylist)
(setq mylist '(1 2 3 4 5 6 7))
(duo-sym-roll-to-end 3 'mylist)
(print mylist)
(setq mylist '((1 . 2) (2 . 4) (3 . 6) (4 . 2) (5 . 3)))
(duo-sym-roll-to-beg 3 'mylist 'duo-x-match-car-p)
(print mylist)
(setq mylist '(1 2 3 4 5 6 7))
(duo-sym-reverse 'mylist)
(print mylist)
(require 'duo-referen)
(setq ref (list '(1 2 3 4 5 6 7)))
(duo-ref-reverse ref)
(print ref)
(print (duo-deref ref))
(require 'duo-return)
(setq mylist '(1 2 3 4 5 6 7))
(setq ret (duo-return-reverse mylist))
(print ret)
#+end_src
* Warning
Despite abundant testing, some bugs might remain, so be careful.
# Local Variables:
# indent-tabs-mode: nil
# End: