#|
Summary


Functions for folding and unfolding a cons of two lists.
   '(() . ())  ==  '(() . nil)  ==  '(())  ==  '(nil)


Any list becomes a foldy by consing nil with it.

   (setq any-list '(one two))

   (cons nil any-list)
     ->  '(() . (one two))
     ==  '(() . (one two . nil))
     ==  '(() one two . ())
     ==  '(() one two)


Folding puts list items from the cdr into the car
 one at a time, thereby in reverse order.

Whereas the cadr was initially the first list item
 (after the empty list), folding results in the next
 cadr thereafter becoming the "current item".

The caar (prior) and cadr (current) expose the middle items
 as the crease in the foldable list.

When there are no more items to fold into the car,
 then the cdr is nil and potentially represents
 either "no current item" or "empty selection",
 if desired.


Moving all the items from the cdr into the list in the car
 essentially flips over (reverses) the whole list.

   '(nil one two)  ==  '(() one two)       ; same

                   ->  '((one) two)        ; creased

                   ->  '((two one))        ; flipped
                   ==  '((two one) . nil)  ; same
                   ==  '((two one) . ())   ; same


Unfolding is the reversal of folding,
 moving items from the list in the car into the cdr.

   '((two one))  ->  '((one) two)   ; creased
                 ->  '(() one two)  ; flopped


In other words, a foldy is a normal list of a list.
 Nothing special about one, this is just an approach.
 It is breaking a list in half and exposing its middle,
 thereby retaining the current position, too.
All done within itself, emerging naturally with no need
 for additional variables.
|#


#|
This emerges in the universe on its own, as all has.
|#


#|
Content outline


* Requirements

* Premise, an intro to #'cons and nil

* Basic approaches to folding and unfolding

* General function use and naming conventions


* Ancillary functions for a foldable list, a.k.a. foldy.
  Independent from each other.

 * Example of using these functions

 * defun fold-one.nu
    Move first item of second half to first half.

 * defun fold-until.nu
    Move some items from second half to first half.

 * defun fold-all.nu
    Move all items from second half to first half.

 * defun unfold-one.nu
    Move first item of first half to second half.

 * defun unfold-until.nu
    Move some items from first half to second half.

 * defun unfold-all.nu
    Move all items from first half to second half.

|#


#|#
Requirements


This was experienced within:
 Steel Bank Common Lisp (SBCL) 2.1.0
 sbcl.org
|#


#|#
Premise, an intro to #'cons and nil.


Embracing items with parentheses
 acknowledges those items
 are associated with each other.

Nil is an empty list,
 as in "nothing in list".
 But that also asserts "Nil Is a List"
 rather than nothingness itself.
 It has become the mnemonic name
 for an empty set of parentheses.
   nil  ==  ()
        ==  '() ; quoted form

A list of one item
 is never notated as a list,
 t.i. it has no parentheses.
 It is written simply as the item itself,
 for there are no other items associated.

A list of more than one item
 is notated as a dotted list,
 with a spaced separated period
 before its final item.
   '(one two . three)

A list with nil as its final item
 is commonly abbreviated
 without dotting with nil.
   '(one . nil)  ==  '(one)

A list of items without
 a dotted final item
 has visually consistent
 delimitation, thereby
 is a "proper list".
But as a list without
 a dotted final item,
 a proper list is merely
 the abbreviated form
 for its items dotted with nil
 as the final item.
   '(one)  ==  '(one . nil)

As such, a proper list with only nil
 is a list with an empty list:
   '(nil)  ==  '(())
 but is also the abbreviated form
 for a list of items dotted with nil
 as its final item:
   '(nil)  ==  '(nil . nil)
 which is a dotted list of two lists:
   '(nil)  ==  '(() . ())


Consing constructs a dotted list.
   (cons 'one 'two)
     ->  '(one . two)

Multiple consing results in
 the last item added
 becoming the first.
   (cons 'three       ; second cons
    (cons 'one 'two)) ; first cons
      ->  '(three one . two)

Consing an item with nil
 results in a proper list,
 because a proper list
 is the abbreviated form
 for its items dotted with nil
 as its final item.
   (cons 'three            ; third cons
    (cons 'two             ; second cons
     (cons 'one nil)))     ; first cons
       ->  '(three two one . nil)
       ==  '(three two one . ())
       ==  '(three two one)


A proper list beginning with nil
 begins and ends with a list.
   (cons nil '(one))
     ->  '(nil one)
     ==  '(nil one . nil)
     ==  '(()  one .  ())
|#


#|#
Basic approaches to folding and unfolding.


There is nothing special about a foldy,
 because it is simply a list of a list.

Basic shifting can be done by copying
 from one list into the other before removing,
 or remove then insert in the other list.

When the list of items are all in the cdr,
 then it is considered completely unfolded.
 Therefore, the first item of the list
 is the car of the cdr.
As such, the car of the cdr is generally considered
 the current item of the list.

When the list of items are all in the car,
 then it is considered completely folded.
 Therefore, the current item is nil,
 t.i. there is no current item.
That can represent an empty selection.


For example, fold an item from the second half
 to the first.

   ; Add to first half an item from second half.
   (setf (car foldy)
    (cons
     (cadr foldy)
     (car foldy)))

   ; Drop the first item of second half.
   (setf (cdr foldy)
    (cddr foldy))


Unfolding an item from the first half
 is simply the reverse.

   ; Add to second half an item from first half.
   (setf (cdr foldy)
    (cons
     (caar foldy)
     (cdr foldy)))

   ; Drop the first item of first half.
   (setf (car foldy)
    (cdar foldy))


Or, consider using the #'push and #'pop macros.

   ; Fold:
   ;  Move an item from second half to first half.
   (push
    (pop (cdr foldy))
    (car foldy)

   ; Unfold:
   ;  Move an item from first half to second half.
   (push
    (pop (car foldy))
    (cdr foldy)
|#


#|
Hereafter, there be code.
|#

#|#
General function use and naming conventions.


".nu": Arbitrary suffix for preventing name collisions,
       as in "named uniquely" or "now unique".


Trace the source of variables by prefixing a letter
 representing that source.

   defun args prefix:  F for "function"
     let args prefix:  L for "let"
  lambda args prefix:  A for "anonymous function"

Combine A and L prefixes by depth, thereby revealing scope
 and discouraging perplexation.  For example:

                       defun args  ->  F prefix
                 defun > let args  ->  L prefix
  defun > let > let > lambda args  ->  LLA prefix
|#


#|#
Functions.


Independent from each other.  Each modifies the foldy
 and then returns the foldy.


For example, consider wrapping around from
 the first item to the last item.  Recall,
 the car of the cdr is generally considered
 the current item of a foldable list.

First, fold everything into the list in the car,
 which essentially reverses the whole list.
 Its first item is the last item of the list.
Then unfolding one item would move the car
 of the reversed list into the cdr, making
 the last item of the list as the current item
 of the list.

   (setq foldy '(() one two three))

   (fold-all.nu foldy)
     -> '((three two one))

   (unfold-one.nu foldy)
     -> '((two one) three)

Or, combine them:

   (unfold-one.nu
    (fold-all.nu foldy))
|#


;#
(defun
fold-one.nu
(Ffoldy)
"Move first item of second half to first half.

Moves an item only when cdr is non-nil.

Modifies original, but also returns foldy
 as a convenience."
 ;;Ensure there is an item in second half to move.
(and(cdr Ffoldy)
 ;;;Move first item of second half to first half.
(push(pop(cdr Ffoldy))(car Ffoldy)))
 ;;Return foldy as convenience for further use.
Ffoldy)


;#
(defun
fold-until.nu
(Ffoldy Fstop)
"Move some items from second half to first half.

Moves an item only when cdr is non-nil.

Fstop is either a lambda or a quoted function
 for #'funcall.  It receives a single item
 (the cadr of Ffoldy) and returns a non-nil value
 for stopping at that item, leaving it as the cadr.

Each moved item becomes the new caar of Ffoldy.

Modifies original, but also returns foldy
 as a convenience."
(do
();no variables
 ;;;Common Lisp #'do is actually "do until non-nil".
((or
 ;;;;;Stop when no more items, t.i. cdr is nil.
(not(cdr Ffoldy))
 ;;;;;Or stop when Fstop returns non-nil,
(funcall Fstop(cadr Ffoldy))))
 ;;;Move first item of second half to first half.
(push(pop(cdr Ffoldy))(car Ffoldy)))
 ;;Return foldy as convenience for further use.
Ffoldy)


;#
(defun
fold-all.nu
(Ffoldy)
"Move all items from second half to first half.

Moves an item only when cdr is non-nil.

Modifies original, but also returns foldy
 as a convenience."
(do
();no variables
 ;;;Common Lisp #'do is actually "do until non-nil".
 ;;; Stop when no more items to move, t.i. cdr is nil.
((not(cdr Ffoldy)))
 ;;;Move first item of second half to first half.
(push(pop(cdr Ffoldy))(car Ffoldy)))
 ;;Return foldy as convenience for further use.
Ffoldy)


;#
(defun
unfold-one.nu
(Ffoldy)
"Move first item of first half to second half.

Moves an item only when car is non-nil.

Modifies original, but also returns foldy
 as a convenience."
 ;;Ensure there is an item in first half to move.
(and(car Ffoldy)
 ;;;Move first item of first half to second half.
(push(pop(car Ffoldy))(cdr Ffoldy)))
 ;;Return foldy as convenience for further use.
Ffoldy)


;#
(defun
unfold-until.nu
(Ffoldy Fstop)
"Move some items from first half to second half.

Moves an item only when car is non-nil.

Fstop is either a lambda or a quoted function
 for #'funcall.  It receives a single item
 (the caar of Ffoldy) and returns a non-nil value
 for stopping at that item, making it the cadr.

Each moved item becomes the new cadr of Ffoldy.

Modifies original, but also returns foldy
 as a convenience."
(do
();no variables
(;;;;Common Lisp #'do is actually "do until non-nil".
(or
 ;;;;;Stop when no more items, t.i. car is nil.
(not(car Ffoldy))
 ;;;;;Or stop when Fstop returns non-nil,
(funcall Fstop(caar Ffoldy)))
 ;;;;Move one more item after completion so as
 ;;;; to coordinate with #'fold-until.nu results.
(and(car Ffoldy)
(push(pop(car Ffoldy))(cdr Ffoldy))))
 ;;;Move first item of first half to second half.
(push(pop(car Ffoldy))(cdr Ffoldy)))
 ;;Return foldy as convenience for further use.
Ffoldy)


;#
(defun
unfold-all.nu
(Ffoldy)
"Move all items from first half to second half.

Moves an item only when car is non-nil.

Modifies original, but also returns foldy
 as a convenience."
(do
();no variables
 ;;;Common Lisp #'do is actually "do until non-nil".
 ;;; Stop when no more items to move, t.i. car is nil.
((not(car Ffoldy)))
 ;;;Move first item of first half to second half.
(push(pop(car Ffoldy))(cdr Ffoldy)))
 ;;Return foldy as convenience for further use.
Ffoldy)