PLEAC examples - 4. Arrays

4.0. Introduction

If you are asked about the contents of your pockets, or the names of the last three presidents, or how to get to the highway, you recite a list: you name one thing after another in a particular order.

Lists are part of your conception of the world.

PicoLisp doesn't support arrays as contiguous pieces of memory. Instead, the standard Lisp linked lists are used.

4.0.1. Example

   : (setq Nested '(this that the other))
   -> (this that the other)

   : (setq Nested '(this that (the other)))
   -> (this that (the other))

   : (setq Tune '("The" "Star-Spangled" "Banner"))
   -> ("The" "Star-Spangled" "Banner")
References setq

4.1. Specifying a List In Your Program

You want to include a list in your program. This is how you initialize arrays.

4.1.1. Example

   : (setq A '("quick" "brown" "fox"))
   -> ("quick" "brown" "fox")

   : (setq A '(Why are you teasing me?))
   -> (Why are you teasing me?)

   # Lines are (here) read from STDIN.
   # Just press a single ENTER to quit.

   : (setq Lines (make (char) (while (line T) (link @))))
   The boy stood on the burning deck,
   It was as hot as glass.
   -> ("he boy stood on the burning deck," "It was as hot as glass.")
References char line link make setq while

4.1.2. Example

Create the file 'mydatafile' containing:
This is the first line.
And this the second.
A third line.
This is the very last line!
Now start PicoLisp and run this code:
   : (setq Bigarray
      (in "mydatafile"
         (make
            (until (eof)
               (link (line T)) ) ) ) )
   -> ("This is the first line." "And this the second." "A third line." "This is the very last line!   ")
References eof in line link make setq until

4.1.3. Example

   : (setq
        PerlHost "www.perl.com"
        HostInfo (in (list 'nslookup PerlHost) (till NIL T)) )
   -> "Server:^I^I192.168.178.1^JAddress:^I192.168.178.1#53^J^JNon-authoritative answer:^Jwww.perl.com^Icanonical name = klb.develooper.com.^JName:^Iklb.develooper.com^JAddress: 207.171.7.45^JName:^Iklb.develooper.com^JAddress: 207.171.7.55^JName:^Iklb.develooper.com^JAddress: 2607:f238:3::1:45^JName:^Iklb.develooper.com^JAddress: 2607:f238:3::1:55^J^J"

   : (split (chop "Costs only $4.95") " ")
   -> (("C" "o" "s" "t" "s") ("o" "n" "l" "y") ("$" "4" "." "9" "5"))

   : (mapcar pack (split (chop "Costs only $4.95") " "))
   -> ("Costs" "only" "$4.95")

   : (setq Ships '("Niña" "Pinta" "Santa María"))
   -> ("Niña" "Pinta" "Santa María")
References chop mapcar pack setq split

4.2. Printing a List with Commas

You'd like to print out a list with an unknown number of elements with an "and" before the last element, and with commas between each element if there are more than two.

4.2.1. Example

   : (pack "The " (glue ", " '(big brown dirty hungry)) " fox")
   -> "The big, brown, dirty, hungry fox"

    : (setq Array '(red yellow green))
   -> (red yellow green)

   : (prinl "I have " Array " marbles.")
   I have redyellowgreen marbles.
   -> " marbles."

   : (prinl "I have " (glue " " Array) " marbles.")
   I have red yellow green marbles.
   -> " marbles."
References glue pack prinl setq

4.2.2. Example

   : (de Lists
      ("just one thing")
      ("Mutt" "Jeff")
      ("Peter" "Paul" "Mary")
      ("To our parents" "Mother Theresa" "God")
      ("pastrami" "ham and cheese" "peanut butter and jelly" "tuna")
      ("recycle tired, old phrases" "ponder big, happy thoughts")
      ("recycle tired, old phrases"
         "ponder big, happy thoughts"
         "sleep and dream peacefully") )
   -> Lists

   : (de commifySeries (Lst)
      (ifn (cddr Lst)
         (glue " and " Lst)
         (glue (if (find '((S) (sub? "," S)) Lst) "; " ", ")
            (conc
               (head -1 Lst)
               (cons (pack "and " (last Lst))) ) ) ) )
   -> commifySeries

   : (for L Lists
      (prinl "The list is: " (commifySeries L) ".") )
   The list is: just one thing.
   The list is: Mutt and Jeff.
   The list is: Peter, Paul, and Mary.
   The list is: To our parents, Mother Theresa, and God.
   The list is: pastrami, ham and cheese, peanut butter and jelly, and tuna.
   The list is: recycle tired, old phrases and ponder big, happy thoughts.
   The list is: recycle tired, old phrases; ponder big, happy thoughts; and sleep and dream peacefully.
   -> "."
References cddr conc cons de find for glue head if ifn last pack prinl sub?

4.3. Changing Array Size

You want to enlarge or truncate an array. For example, you might truncate an array of employees that's already sorted by salary to list the five highest-paid employees.

Or, if you know how big your array will get and that it will grow piecemeal, it's more efficient to get memory for it in one step by enlarging it just once than it is to keep pushing values onto the end.

4.3.1. Example

   : (de whatAboutThatArray ()
      (prinl "The array now has " (length People) " elements.")
      (prinl "Element #4 is `" (get People 4) "'.") )
   -> whatAboutThatArray

   : (de People
      Crosby Stills Nash Young )
   -> People

   : (whatAboutThatArray)
   The array now has 4 elements.
   Element #4 is `Young'.
   -> "'."

   : (con (tail 2 People))
   -> NIL

   : (whatAboutThatArray)
   The array now has 3 elements.
   Element #4 is `'.
   -> "'."

   : (setq People (need -10000 People))
   -> (Crosby Stills Nash NIL NIL NIL NIL ... NIL)

   : (whatAboutThatArray)
   The array now has 10000 elements.
   Element #4 is `'.
   -> "'."
References con de get length need prinl setq tail

4.4. Doing Something with Every Element in a List

You want to repeat a procedure for every element in a list.

Often you use an array to collect information you're interested in; for instance, login names of users who have exceeded their disk quota.

When you finish collecting the information, you want to process it by doing something with every element in the array.

In the disk quota example, you might send each user a stern mail message.

4.3.1. Iterate over a list

   : (setq Mylist '( a b c))
   -> (a b c)

   : (for item Mylist
        (println item))
   a
   b
   c
   -> c

   : (mapc println Mylist)
   a
   b
   c
   -> c
References for println setq

4.3.2. Filter records in a file

First create a small file called 'mydata':
red light
green apples
color red
traffic light
green meadows
Then run this PicoLisp code:
   : (in "mydata"
      (while (line T)
         (and (sub? "green" @) (prinl @)) ) )
   green apples
   green meadows
   -> "green meadows"
References and in line prinl sub? while

4.3.3. Reverse words in lines typed as input

First create a file named 'testflip':
this is a line
and this is another
the birds are swimming
with their mother
Then run the following PicoLisp code:
   : (in "testflip"
      (until (eof)
         (prinl (glue " " (flip (split (line) " "))))))
   line a is this
   another is this and
   swimming are birds the
   mother their with
   -> "mother their with"
References eof flip glue in line prinl split until

4.3.4. Update all array elements

   : (setq Array (1 2 3))
   -> (1 2 3)

   : (map dec Array)
   -> 2

   : Array
   -> (0 1 2)
References dec map setq

4.5. Iterating over an Array by Reference

You have a reference to an array, and you want to use foreach to work with the array's elements.

4.5.1. Example

   : (setq Lst '(a b c))
   -> (a b c)

   : (map
      '((L)
         (println (car L))  # Show the element
         (set L 'busted) )  # Modify the element destructively
      lst)
   a
   b
   c
   -> busted

   : Lst
   -> (busted busted busted)

   : (setq Lst '(a b c))
   -> (a b c)

   : (for I (length Lst)
      (println I (get Lst I)) )
   1 a
   2 b
   3 c
   -> c

   # Or even better ...

   : (for (I . X) Lst
      (println I X) )
   1 a
   2 b
   3 c
   -> c

   : (setq
      Fruits '(Apple Blackberry)
      FruitRef 'Fruits )
   -> Fruits

   : (for Fruit (val FruitRef)
      (prinl Fruit " tastes good in a pie.") )
   Apple tastes good in a pie.
   Blackberry tastes good in a pie.
   -> " tastes good in a pie."
References car for get length map prinl println set setq val

4.6. Extracting Unique Elements from a List

You want to eliminate duplicate values from a list, such as when you build the list from a file or from the output of another command.

This recipe is equally applicable to removing duplicates as they occur in input and to removing duplicates from an array you've already populated.

4.6.1. Example

   : (let Uniq NIL
      (for Item '(a b c b c d c d e d e f)
         (unless (memq Item Uniq)
            (push 'Uniq Item) ) )
      Uniq )
   -> (f e d c b a)

   : (let Seen NIL
      (for Item '(a b c b c d c d e d e f)
         (accu 'Seen Item 1) )
      Seen )
   -> ((f . 1) (e . 2) (d . 3) (c . 3) (b . 2) (a . 1))

   : (uniq '(a b c b c d c d e d e f))
   -> (a b c d e f)
References accu for let memq push uniq unless

4.6.2. Get list of unique users in system

First create the list of users in the Linux shell:
awk -F':' '{ print $1}' /etc/passwd >users
Then run this code in PicoLisp:
   : (in "users"
       (uniq
         (make
           (while (split (line) " ")
             (link (pack (car @))) ) ) ) )
   -> ("root" "daemon" "bin" "sys" "sync" "games" "man" "lp" "mail" "news" "uucp" "proxy" "www-data" "backup" "list" "irc" "gnats" "nobody" "systemd-network" "systemd-resolve" "syslog" "messagebus" "_apt" "lxd" "uuidd" "dnsmasq" "landscape" "sshd" "pollinate" "arie")

   # Print the list sorted alphabetically

   : (println 'Users 'logged 'in: (sort @))
   Users logged in: ("_apt" "arie" "backup" "bin" "daemon" "dnsmasq" "games" "gnats" "irc" "landscape" "list" "lp" "lxd" "mail" "man" "messagebus" "news" "nobody" "pollinate" "proxy" "root" "sshd" "sync" "sys" "syslog" "systemd-network" "systemd-resolve" "uucp" "uuidd" "www-data")
   -> ("_apt" "arie" "backup" "bin" "daemon" "dnsmasq" "games" "gnats" "irc" "landscape" "list" "lp" "lxd" "mail" "man" "messagebus" "news" "nobody" "pollinate" "proxy" "root" "sshd" "sync" "sys" "syslog" "systemd-network" "systemd-resolve" "uucp" "uuidd" "www-data")
References car in line link make pack split uniq while

4.7. Finding Elements in One Array but Not Another

You want to eliminate duplicate values from a list, such as when you build the list from a file or from the output of another command.

This recipe is equally applicable to removing duplicates as they occur in input and to removing duplicates from an array you've already populated.

4.7.1. Example

   # Intersection (common elements) of two lists

   : (sect '(a b c d e f) '(a c f h))
   -> (a c f)

   # Use symbols for unique keys

   : (setq key1 1  key2 2)
   -> 2

   : key1
   -> 1

   : key2
   -> 2

   # Using properties

   : (put 'Hash 'key1 1)
   -> 1

   : (put 'Hash 'key2 2)
   -> 2

   : (get 'Hash 'key1)
   -> 1

   : (get 'Hash 'key2)
   -> 2

   # Using association Lists

   : (de Hash (key1 . 1) (key2 . 2))
   -> Hash

   : (assoc 'key1 Hash)
   -> (key1 . 1)

   : (asoq 'key1 Hash)
   -> (key1 . 1)

   : (get Hash 'key1)
   -> 1

   # Using an index tree

   : (idx 'Hash '(key1 . 1) T)
   -> ((key1 . 1) (key2 . 2))

   : (idx 'Hash '(key2 . 2) T)
   -> NIL

   : (lup Hash 'key1)
   -> (key1 . 1)
References asoq assoc de get idx lup put sect setq

4.8. Union, Intersection and Difference of Unique Lists

You have a pair of lists, each having unduplicated items. You'd like to find out which items are in both lists (intersection), one but not the other (difference), or either (union).

4.8.1. Example

   # Create two sets

   : (setq
      A (1 3 5 6 7 8)
      B (2 3 5 7 9) )
   -> (2 3 5 7 9)

   # Union of the sets

   : (uniq (append A B))
   -> (1 3 5 6 7 8 2 9)

   # Intersection of the sets

   : (sect A B)
   -> (3 5 7)

   # Difference of the sets

   : (diff A B)
   -> (1 6 8)
References append diff sect setq uniq

4.9. Appending One Array to Another

You want to join two arrays by appending all the elements of one to the end of the other.

4.9.1. Example

   # Initialize two Lists

   : (setq
      Members '(Time Flies)
      Initiates '(An Arrow) )
   -> (An Arrow)

   : Members
   -> (Time Flies)

   : Initiates
   -> (An Arrow)

   # Non destructive append of the lists

   : (append Members Initiates)
   -> (Time Flies An Arrow)

   : Members
   -> (Time Flies)

   : Initiates
   -> (An Arrow)

   # Destructive append of the lists

   : (conc Members Initiates)
   -> (Time Flies An Arrow)

   : Members
   -> (Time Flies An Arrow)     # Members is overwritten

   : Initiates
   -> (An Arrow)

   # Non destructive insert of an element at some position

   : (insert 3 Members 'Like)
   -> (Time Flies Like An Arrow)

   : Members
   -> (Time Flies An Arrow)

   # Destructively change the first element of a list

   : (set Members 'Fruit)
   -> Fruit

   : Members
   -> (Fruit Flies An Arrow)

   # Destructively change the last element of a list

   : (set (tail 1 Members) 'Banana)
   -> Banana

   : Members
   -> (Fruit Flies An Banana)
References append conc insert set setq tail

4.10. Reversing an Array

You want to reverse an array.

4.10.1. Example


   # Note that a destructive function changes one or more of its
   # actual parameter destructively, but returns the desired result.

   # Create an example list

   : (setq List (5 2 8 1 7 9 3 4 6))
   -> (5 2 8 1 7 9 3 4 6)

   # Reverse the element non destructively

   : (reverse List)
   -> (6 4 3 9 7 1 8 2 5)

   : List
   -> (5 2 8 1 7 9 3 4 6)

   # Reverse the element destructively

   : (flip List)
   -> (6 4 3 9 7 1 8 2 5)

   : List
   -> (5)

   # Reset the example list

   : (setq List (5 2 8 1 7 9 3 4 6))
   -> (5 2 8 1 7 9 3 4 6)

   # Sort the list destructively ASCENDING

   : (sort List)
   -> (1 2 3 4 5 6 7 8 9)

   : List
   -> (5 6 7 8 9)

   # Reset the example list

   : (setq List (5 2 8 1 7 9 3 4 6))
   -> (5 2 8 1 7 9 3 4 6)

   # Sort the list destructively DESCENDING

   : (by - sort List)
   -> (9 8 7 6 5 4 3 2 1)

   : List
   -> (5 2 8 1 7 9 3 4 6)

   # Showing that PicoLisp can sort lists of mixed type elements

   : (flip (sort '(a 3 1 (1 2 3) d b 4 T NIL (a b c) (x y z) c 2)))
   -> (T (x y z) (a b c) (1 2 3) d c b a 4 3 2 1 NIL)
References by flip reverse setq sort

4.11. Processing Multiple Elements of an Array

You want to pop or shift multiple elements at a time.

4.11.1. Example

   # Destructively cut a number of elements off of the front of a list

   : (setq List '(a b c d e f g))
   -> (a b c d e f g)

   : (cut 3 'List)
   -> (a b c)

   : List
   -> (d e f g)

   # Alters the list; chop off the last 3 elements

   : (setq List '(a b c d e f g))
   -> (a b c d e f g)

   : (tail 3 List)                 # Non destructive
   -> (e f g)

   : List
   -> (a b c d e f g)

   : (con (nth List 4))            # Destructive
   -> NIL

   : List
   -> (a b c d)

   # Pop elements off a list

   : (setq
      Friends '(Peter Paul Mary Jim Tim)
      This (pop 'Friends)
      That (pop 'Friends) )
   -> Paul

   : This
   -> Peter

   : That
   -> Paul

   : Friends
   -> (Mary Jim Tim)

   # Another example

   : (setq
      Beverages '(Dew Jolt Cola Sprite Fresca)
      Pair (tail 2 Beverages)         # Non destructive
      Beverages (head 3 Beverages) )  # Destructive
   -> (Dew Jolt Cola)

   : Pair
   -> (Sprite Fresca)

   : Beverages
   -> (Dew Jolt Cola)
References con cut head nth pop setq tail

4.12. Find First Element that passes a Test

You want the first element in the list (or its index) that passes a test.

Alternatively, you want to know whether any element passes the test.

The test can be simple identity ("Is this element in the list?")[1] or more complex ("I have a list of Employee objects, sorted from highest salary to lowest. Which manager has the highest salary?").

Simple cases normally only require the value of the element, but when the array itself will be altered. You probably need to know the index number of the first matching element.

4.12.1. Example

   # Initialize array of employees

   :  (setq Employees '(emp1 emp2 emp3 emp4 emp5))
   -> (emp1 emp2 emp3 emp4 emp5)

   # Give all employees a name property

   : (mapc put
         Employees
         '(name .)
         '("Abel" "Jones" "Millner" "Noles" "Zaphod") )
   -> "Zaphod"

   # Give all employees a category property

   : (mapc put
         Employees
         '(category .)
         '(engineer cook teacher engineer vicar) )
   -> vicar

   # Now show the employees with all their properties

   : (mapc show Employees)
   emp1 NIL
      category engineer
      name "Abel"
   emp2 NIL
      category cook
      name "Jones"
   emp3 NIL
      category teacher
      name "Millner"
   emp4 NIL
      category engineer
      name "Noles"
   emp5 NIL
      category vicar
      name "Zaphod"
   -> emp5

   # ------------------------------------------------------------------
   # Find first employee with a 'category of 'engineer
   # ------------------------------------------------------------------
   # This example deserves a bit of extra explanation.
   #
   # The syntax of 'with' is (with 'sym . prg) -> any
   #
   # In this case 'sym' is the result of the 'find' operation,
   # being one of the employees in the list.
   #
   # The function 'with' first saves the current 'This' object and
   # then sets this special variable 'This' to the result of the
   # 'find' function. When 'prg' is finished 'with' resets 'This'
   # to its original value (from before the 'with' call) which
   # is totally irrelevant in this case.
   #
   # So, when 'prg' (the 'prinl') is being executed, 'This' points
   # to the employee just found. Hence the function calls to ':'
   # will work properly.

   # ------------------------------------------------------------------
   # Another gem in this code:
   # ------------------------------------------------------------------
   # The first argument of the 'find' function is an anonymous function
   # (lambda expression). Such a function always starts with a list of
   # the formal parameters, but in this case the one formal parameter
   # is named 'This'.
   #
   # When using lexical scoping that name would be totally insignificant
   # but PicoLisp is dynamically scoped!
   #
   # This implies that 'This' which is a valid formal parameter actually
   # corresponds to the global special variable 'This'.
   #
   # And that in turn makes it useful, because we now can use the ':'
   # function, which refers to a property of the current object, which
   # thus happens to be stored in the variable 'This'.
   #
   # Nice huh?

   : (with
         (find                                            # sym
            '((This) (== 'engineer (: category)))
            Employees )
         (prinl "First engineer found is: " (: name))  )  # prg
   First engineer found is: Abel
   -> "Abel"
References : == find mapc prinl put setq show This with

4.13. Find All Elements that Match Certain Criteria

From a list, you want only the elements that match certain criteria.

This notion of extracting a subset of a larger list is common.

It's how you find all engineers in a list of employees, all users in the "staff " group, and all the filenames you're interested in.

4.13.1. Example

   : (de Nums 84598 4439223 248749 2488711 233716 3375644 211118)
   -> Nums

   : (filter '((N) (> N 1000000)) Nums)
   -> (4439223 2488711 3375644)

   : (filter > Nums (1000000 .))
   -> (4439223 2488711 3375644)
References > de filter

4.13.2. Employees revisited...

   : (setq Employees '(emp1 emp2 emp3 emp4 emp5))
   -> (emp1 emp2 emp3 emp4 emp5)

   : (mapc put
            Employees
            '(name .)
            '("Abel" "Jones" "Millner" "Noles" "Zaphod") )
   -> "Zaphod"

   : (mapc put
            Employees
            '(position .)
            '(engineer cook teacher engineer vicar) )
   -> vicar

   : (mapc show Employees)
   emp1 NIL
      position engineer
      name "Abel"
   emp2 NIL
      position cook
      name "Jones"
   emp3 NIL
      position teacher
      name "Millner"
   emp4 NIL
      position engineer
      name "Noles"
   emp5 NIL
      position vicar
      name "Zaphod"
   -> emp5

   # See 4.12.1. for better explanation

   : (filter '((This) (== 'engineer (: position))) Employees)
   -> (emp1 emp4)
References : == filter mapc put setq show This

4.14. Sorting an Array Numerically

You want to sort a list of numbers.

Note: PicoLisp 'sort' destructively alters the input list!

4.14.1. Example

   # Standard ASCENDING sort

   : (setq Lst (23 12 54 11 19 1 4 47))
   -> (23 12 54 11 19 1 4 47)

   : (sort Lst)
   -> (1 4 11 12 19 23 47 54)

   : Lst
   -> (23 47 54)              # 'sort' is destructive!

   # Use 'flip' to reverse the outcome of the 'sort'
   : (setq Lst (23 12 54 11 19 1 4 47))
   -> (23 12 54 11 19 1 4 47)

   : (flip (sort Lst))
   -> (54 47 23 19 12 11 4 1)

   : Lst
   -> (23 19 12 11 4 1)       # 'sort' is destructive!

   # Use a helper function (like '>') to determine
   # the desired sort order.

   : (setq Lst (23 12 54 11 19 1 4 47))
   -> (23 12 54 11 19 1 4 47)

   : (sort Lst >)
   -> (54 47 23 19 12 11 4 1)

   : Lst
   -> (23 19 12 11 4 1)       # 'sort' is destructive!

   # Standard DESCENDING sort

   : (setq Lst (23 12 54 11 19 1 4 47))
   -> (23 12 54 11 19 1 4 47)

   : (by - sort Lst)
   -> (54 47 23 19 12 11 4 1)

   : Lst
   -> (23 12 54 11 19 1 4 47) # 'sort' is destructive!
References - > by flip setq sort

4.15. Sorting a List by Computable Field

You want to sort a list by something more complex than a simple string or numeric comparison.

This is common when working with objects ("sort by the employee's salary") or complex data structures ("sort by the third element in the array that this is a reference to").

It's also applicable when you want to sort by more than one key, for instance, sorting by birthday and then by name when multiple people have the same birthday.

4.15.1. Sort using a helper function

   # Same as 14.4.1. where we did (by - sort Lst)
   # In this case we sort on the 'cdr' part.
   # The cdrs before are: (7 3 1 2)

   : (by cdr sort '((4 . 7) (19 . 3) (8 . 1) (4 . 2)))
   -> ((8 . 1) (4 . 2) (19 . 3) (4 . 7))

   # The cdrs after are now in sorted order: (1 2 3 7)
   # But, most important, this sorts ON the cdr, but
   # orders the pairs!

   # Although the next code fragment does exacly the same,
   # it is not recommended, because it is slower and needs
   # more code to do the same.

   : (sort
      '((4 . 7) (19 . 3) (8 . 1) (4 . 2))
      '((X Y) (> (cdr Y) (cdr X))) )
   -> ((8 . 1) (4 . 2) (19 . 3) (4 . 7))
References > cdr by sort

4.15.2. Employees revisited again ...

   # Create Employees and their properties

   : (setq Employees '(emp1 emp2 emp3 emp4 emp5))
   -> (emp1 emp2 emp3 emp4 emp5)

   : (mapc put
          Employees
          '(name .)
          '("Jones"  "Zaphod" "Millner" "Abel" "Noles") )
   -> "Noles"

   : (mapc put
          Employees
          '(salary .)
          '(50000 40000 60000 30000 45000) )
   -> 45000

   : (mapc put
          Employees
          '(age .)
          '(50 30 28 54 45) )
   -> 45

   : (mapc show Employees)
   emp1 NIL
      age 50
      salary 50000
      name "Jones"
   emp2 NIL
      age 30
      salary 40000
      name "Zaphod"
   emp3 NIL
      age 28
      salary 60000
      name "Millner"
   emp4 NIL
      age 54
      salary 30000
      name "Abel"
   emp5 NIL
      age 45
      salary 45000
      name "Noles"
   -> emp5

   : (for This (by '((This) (: name)) sort Employees)
       (prinl (: name) " earns $" (: salary)) )
   Abel earns $30000
   Jones earns $50000
   Millner earns $60000
   Noles earns $45000
   Zaphod earns $40000
   -> 40000

   # Sort using an anonymous function.
   # In this case this function creates pairs of the name and age properties,
   # and 'sort' sorts on them. The ultimate order is the same as when we
   # would have sorted only on the 'name'.

   : (by '((This) (cons (: name) (: age))) sort Employees)
   -> (emp4 emp1 emp3 emp5 emp2)
References : by cons for mapc prinl put setq show sort This

4.15.3. Other examples of sorting using helper functions

   # Sort on the second position of the names

   : (setq Names '(Armstrong McCarthy Iverson Kay Hickey Rakocevic))
   -> (Armstrong McCarthy Iverson Kay Hickey Rakocevic)

   : (by '((S) (cadr (chop S))) sort Names)
   -> (Kay Rakocevic McCarthy Hickey Armstrong Iverson)

   # Sort on the length of the names

   : (by length sort Names)
   -> (Kay Hickey Iverson McCarthy Armstrong Rakocevic)

   # A bit complicated example.
   # It creates a new list, containing in order:
   #    1. the 4th elemnt of the input line
   #    2. the 3rd element of the input line
   #    3. the first element of the input line
   # where elements are split at ':' characters,
   # and the 3 new elements in the output list are split
   # into lists of single characters.

   : (sort
       (in "passwd"
         (make
           (while (split (line) ":")
             (link (mix @ 4 3 1)) ) ) ) )
-> ((("0") ("0") ("r" "o" "o" "t")) (("1") ("1") ("d" "a" "e" "m" "o" "n")) (("1") ("2" "0" "1") ("p" "a" "u" "l")) (("1" "1") ("1" "1") ("l" "p")))
References by cadr chop in length line link make mix setq sort split while

4.16. Implementing a Circular List

You want to create and manipulate a circular list.

4.16.1. Example

   : (circ 'a)
   -> (a .)

   : (circ 'a 'b 'c)
   -> (a b c .)

   : (rot @)
   -> (c a b .)
References circ rot

4.17. Randomizing the order in an Array

You want to shuffle the elements of an array randomly.

The obvious application is writing a card game, where you must shuffle a deck of cards, but it is equally applicable to any situation where you want to deal with elements of an array in a random order.

4.17.1. Example

   : (setq Lst (12 5 2 9 4 20 101 3 12))
   -> (12 5 2 9 4 20 101 3 12)

   : (by '(NIL (rand)) sort Lst)
   -> (4 2 5 12 101 20 12 3 9)
References by rand setq sort

4.18. Show words column by column on a page

Have you ever wondered how programs like ls generate columns of sorted output that you read down the columns instead of across the rows?

For example:
awk      cp       ed       login    mount    rmdir    sum
basename csh      egrep    ls       mt       sed      sync
cat      date     fgrep    mail     mv       sh       tar
chgrp    dd       grep     mkdir    ps       sort     touch
chmod    df       kill     mknod    pwd      stty     vi
chown    echo     ln       more     rm       su

4.18.1. Example

Fist create the input file called 'colwords' containing:
awk
basename
cat
chgrp
chmod
chown
cp
csh
date
dd
df
echo
ed
egrep
fgrep
grep
kill
ln
login
ls
mail
mkdir
mknod
more
mount
mt
mv
ps
pwd
rm
rmdir
sed
sh
sort
stty
su
sum
sync
tar
touch
vi
then create a PicoLisp script named 'colshow':
#!/usr/bin/picolisp /usr/lib/picolisp/lib.l
# words - gather lines, present in columns

(setq
   Data (in "colwords"
          (make
            (until (eof)
              (link (line T)) ) ) )
   Maxlen (inc (length (maxi length Data)))
   Cols (max (/ (or (format (sys "COLUMNS")) 80) Maxlen) 1)
   Rows (/ (+ (length Data) Cols) Cols)
   Data (make (while Data (link (cut Rows 'Data)))) )

(while (find bool Data)
   (map
      '((D) (space (- Maxlen (length (prin (pop D))))))
      Data )
   (prinl) )

(bye)
and then execute those Linux commands:
chmod 777 colshow
./colshow
which shows the following:
awk       cp        ed        login     mount     rmdir     sum
basename  csh       egrep     ls        mt        sed       sync
cat       date      fgrep     mail      mv        sh        tar
chgrp     dd        grep      mkdir     ps        sort      touch
chmod     df        kill      mknod     pwd       stty      vi
chown     echo      ln        more      rm        su
References - / + bool bye cut eof find format in inc length line link make map max maxi or pop prin prinl setq space sys until while

4.19. Program: permute

Have you ever wanted to generate all possible permutations of an array or to execute some code for every possible permutation? Example: permutate (1 2 3 ) -> (1 2 3) (1 3 2) (2 1 3) (2 3 1) (3 1 2) (3 2 1)

4.19.1. Example

   : (de permute (Lst)
      (ifn (cdr Lst)
         (cons Lst)
         (mapcan
            '((X)
               (mapcar
                  '((Y) (cons X Y))
                  (permute (delete X Lst)) ) )
            Lst ) ) )
   -> permute

   : (mapc println (permute '(man bites dog)))
   (man bites dog)
   (man dog bites)
   (bites man dog)
   (bites dog man)
   (dog man bites)
   (dog bites man)
   -> (dog bites man)
References cdr cons de delete ifn mapc mapcan mapcar println

https://picolisp.com/wiki/?pcepleacarrays

23jun18    ArievW