{"id":13894636,"url":"https://github.com/chimay/duo","last_synced_at":"2025-07-17T09:33:43.136Z","repository":{"id":153608472,"uuid":"176241352","full_name":"chimay/duo","owner":"chimay","description":"Library of in place list operations in Emacs-Lisp","archived":false,"fork":false,"pushed_at":"2023-09-26T15:04:14.000Z","size":206,"stargazers_count":9,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-08-07T18:31:12.400Z","etag":null,"topics":["elisp","inplace","list","operation"],"latest_commit_sha":null,"homepage":null,"language":"Emacs Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chimay.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2019-03-18T08:54:37.000Z","updated_at":"2023-09-26T15:04:18.000Z","dependencies_parsed_at":"2024-04-08T15:50:35.211Z","dependency_job_id":"c6e557d7-6bfa-4ae5-9d39-5d37130b84f9","html_url":"https://github.com/chimay/duo","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chimay%2Fduo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chimay%2Fduo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chimay%2Fduo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chimay%2Fduo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chimay","download_url":"https://codeload.github.com/chimay/duo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226248375,"owners_count":17595159,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elisp","inplace","list","operation"],"created_at":"2024-08-06T18:01:40.253Z","updated_at":"2024-11-24T23:31:17.247Z","avatar_url":"https://github.com/chimay.png","language":"Emacs Lisp","funding_links":[],"categories":["Emacs Lisp"],"sub_categories":[],"readme":"\n#+STARTUP: showall\n\n* Table of contents                                                     :TOC_2_gh:\n- [[#introduction][Introduction]]\n- [[#goal][Goal]]\n  - [[#use-cons-as-double-pointers][Use Cons as double pointers]]\n  - [[#keep-it-simple][Keep It Simple]]\n- [[#summary-of-available-functions][Summary of available functions]]\n- [[#technical-details][Technical details]]\n  - [[#modify-the-argument-list][Modify the argument list]]\n  - [[#files][Files]]\n  - [[#circular-lists][Circular lists]]\n  - [[#next--previous-vs-after--before][Next / Previous vs After / Before]]\n  - [[#remove-vs-delete][Remove vs Delete]]\n  - [[#functions-in-arguments][Functions in arguments]]\n  - [[#assoc][Assoc]]\n- [[#examples][Examples]]\n- [[#warning][Warning]]\n\n* Introduction\n\nDuo is a library of in place list operations in Emacs-Lisp. Its functions modify the\noriginal list when :\n\n  - It’s easy to get back : rotate, reverse, move, etc\n\n  - The name is clear : push, pop, add, drop, insert, remove, etc\n\n    + When an element is removed, a reference to it is often returned\n\nHowever, when it’s difficult or impossible to reverse the operation, a\nnew list is created, with copies of the values using =purecopy=.\nFor instance :\n\n  - filter\n\n  - partition\n\nIf in doubt, check their doc.\n\n\n* Goal\n\n\n** Use Cons as double pointers\n\nThis library is implemented with =(CAR . CDR)= =cons=, which are\ncalled =duo=, hence the name. These cons are everywhere in Elisp,\neither explicitely created or as brick components in lists. A list\nvariable =list= is itself the cons at the beginning of the list.\n=(cdr list)= is the second cons in the list. And so one with\n=(cddr list)=, =(nthcdr N list)=. Generally speaking, a member of\na list is a cons =(value . next-member-in-list)=. Most of Elisp\nis built around these =(CAR . CDR)= double pointers. You can even\nconstruct binary trees with it.\n\n\n** Keep It Simple\n\nPrimarily use Elisp tools from C source code :\n\n  - Built-ins :\n\n    + =cons=, =list=\n\n    + =car=, =cdr=\n\n    + =setcar=, =setcdr=\n\n    + =nth=, =nthcdr=\n\n    + etc\n\n  - Special forms :\n\n    + =if=, =cond=\n\n    + =while=\n\n    + etc\n\nSome functions defined in =subr.el= are also used.\n\n\n* Summary of available functions\n\n  - Finding cons in list\n\n  - Finding previous / next cons\n\n    + In plain list\n\n    + In virtual circular list\n\n  - Replace element\n\n  - Map\n\n  - Join\n\n  - Truncate\n\n  - Next / Previous in group\n\n  - Next / Previous matching filter\n\n  - Push, Add\n\n  - Pop, Drop\n\n  - Rotate \u003c- or -\u003e\n\n  - Roll until a cons is first or last\n\n  - Reverse\n\n  - Insert\n\n  - Remove, Delete\n\n    + One occurence\n\n    + All occurence of a value\n\n  - Teleport : move after or before another cons or element\n\n  - Move previous or next\n\n    + In plain list\n\n    + In virtual circular list\n\n  - Exchange cons or elements\n\n  - Insert in sorted list\n\n  - Insert at group beginning or end\n\n  - Partition with a key function to form an alist\n\n\n* Technical details\n\n\n** Modify the argument list\n\nWhen you pass a list as argument of a function, the calling scope\n=list-var= holds the address of the first cons of the list. The\nargument =arg-list-var= holds a copy of it. Using ~(setq list ...)~\ninside the definition of the function changes the argument list\nreference, not the calling scope one. So, the calling scope address is\nnot updated. As a result, you need either :\n\n  - to use the list symbol in argument (=*-sym-*= functions)\n\n    + ~(function ... 'list ...)~\n\n  - to pass a reference to the list as argument (=*-ref-*= functions)\n\n    + ~(setq reflist (cons list nil))~\n\n    + ~(function ... reflist ...)~\n\n  - to recover the modified list as the returned value (=*-return-*= functions)\n\n    + ~(setq list (function ... list ...))~\n\nA common case of this situation is with functions which modify the\nfirst cons of the list : push, pop, etc.\n\nCheck their doc to know how to recover the updated list.\n\n\n** Files\n\n  - =duo-common.el= holds the functions which don’t modify the list\n\n  - =duo-symbol.el= holds the =*-sym-*= functions\n\n  - =duo-referen.el= holds the =*-ref-*= functions\n\n  - =duo-return.el= holds the =*-return-*= functions\n\n\n** Circular lists\n\nCaution : applying some of these functions to circular lists would\nproduce infinite loops.\n\nHowever, some functions, like =*-circ-*= or =*-rotate-*=, simulate\nvirtual circular lists by :\n\n  - Continuing at the beginning once arrived at the end\n\n  - Continuing at the end once arrived at the beginning\n\n\n** Next / Previous vs After / Before\n\nThere is a slight difference between next/previous and after/before\nfunctions :\n\n  - Next / Previous use a cons as main argument\n\n  - After / Before use the value of an element of the list as main argument\n\n\n** Remove vs Delete\n\nThere is a slight difference between remove and delete functions :\n\n  - Remove removes a cons given as argument\n\n  - Delete remove the first cons whose car matches an element given as argument\n\n\n** Functions in arguments\n\nSome functions accept a function =fn-*= in argument. Among these\n=fn-*=, some takes two arguments. When this is the case, they are\ncalled internally like this :\n\n#+begin_src emacs-lisp\n(funcall fn-* elem-or-cons-from-argument cons-from-loop)\n#+end_src\n\n\n** Assoc\n\nThe classic =assoc= function return the cons =(key . value)=, which is\nthe /content/ of the Alist element, whereas the =duo-assoc= function\nreturn the duo =((key . value) . next-member-in-alist)=, real member of\nthe Alist.\n\n\n* Examples\n\n#+begin_src emacs-lisp\n  (require 'duo-common)\n\n  (setq mylist '(1 2 3 4 5 6 7))\n  (print (duo-slice mylist 3 -1))\n\n  (setq mylist '(1 2 3 4 5 6 7 1 1))\n  (duo-replace-all 1 2 mylist)\n  (print mylist)\n\n  (require 'duo-symbol)\n\n  (setq mylist '(1 2 3 4 5 6 7))\n  (duo-sym-rotate-left 'mylist)\n  (print mylist)\n  (duo-sym-rotate-right 'mylist)\n  (print mylist)\n\n  (setq mylist '(1 2 3 4 5 6 7))\n  (duo-sym-roll-to-end 3 'mylist)\n  (print mylist)\n\n  (setq mylist '((1 . 2) (2 . 4) (3 . 6) (4 . 2) (5 . 3)))\n  (duo-sym-roll-to-beg 3 'mylist 'duo-x-match-car-p)\n  (print mylist)\n\n  (setq mylist '(1 2 3 4 5 6 7))\n  (duo-sym-reverse 'mylist)\n  (print mylist)\n\n  (require 'duo-referen)\n\n  (setq ref (list '(1 2 3 4 5 6 7)))\n  (duo-ref-reverse ref)\n  (print ref)\n  (print (duo-deref ref))\n\n  (require 'duo-return)\n\n  (setq mylist '(1 2 3 4 5 6 7))\n  (setq ret (duo-return-reverse mylist))\n  (print ret)\n#+end_src\n\n* Warning\n\nDespite abundant testing, some bugs might remain, so be careful.\n\n# Local Variables:\n# indent-tabs-mode: nil\n# End:\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchimay%2Fduo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchimay%2Fduo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchimay%2Fduo/lists"}