;;;; -*- lisp -*-

;;;; @@PLEAC@@_NAME
;;;; @@SKIP@@ Common Lisp

;;;; @@PLEAC@@_WEB
;;;; @@SKIP@@ http://www.lisp.org/

;;;; @@PLEAC@@_INTRO
;;;; @@SKIP@@ Various submissions rely on third party http://cliki.net/ASDF packages (e.g. cl-ppcre,
;;;; @@SKIP@@ soundex, etc) and specific helper utility functions (WHEN-LET, CHOMP, etc). Used ASDF
;;;; @@SKIP@@ packages are assumed to be installable via http://cliki.net/ASDF-INSTALL and helper
;;;; @@SKIP@@ utilities are available in the appendix.
;;;; @@SKIP@@ Most of the demonstrated solutions are free from low-level Common Lisp optimizations
;;;; @@SKIP@@ (no type declarations, missing tail-call-optimizations, etc) for the sake of simplicity.
;;;; @@SKIP@@ While this doesn't mean such programs would perform poorly, in certain situations
;;;; @@SKIP@@ particular optimizations might be necessary.
;;;; @@SKIP@@ It should  also be noted that,  most of the scripts are given in plain function form.
;;;; @@SKIP@@ For how to execute these functions using a script file, please consult to your
;;;; @@SKIP@@ implementation's http://en.wikipedia.org/wiki/Shebang_(Unix) documentation.
;;;; @@SKIP@@ (You may also be interested in trivial-shell ASDF package for a lightweight shell
;;;; @@SKIP@@  interaction layer.)

;;;; @@PLEAC@@_APPENDIX
(defun chomp (string)
  "Similar  to Perl's chomp(),  although it  returns the  new value  of `STRING'
rather than the number of characters removed, and doesn't modify its argument."
  (string-right-trim #(#\Newline #\Return) string))

(defmacro when-let ((var value) &body body)
  "Evaluate  `VALUE', and  bind  it to  `VAR'.  When `VALUE'  evaluates to  some
non-NIL value, evaluate `BODY' in the same binding scope."
  `(let ((,var ,value)) (when ,var ,@body)))

(defmacro perl-grep (sequence &body predicate-body)
  "Like Perl's grep.  Predicate is a body  of code that can refer to `IT' as the
current element of the list."
  `(remove-if-not #'(lambda (it) ,@predicate-body) ,sequence))

;; The following could be made more efficient by using a faster TEST
;; function if the keys appear to be simpler.
(defun mkhash (&rest keys/values)
  "Utility for making new EQUAL hashes easily, similar to Perl's
built-in funcionality."
  (let ((newhash (make-hash-table :test 'equal ; use EQUAL so strings work as keys
                                  :size (truncate (/ (length keys/values)
                                                     2)))))
    (loop
       for key in keys/values by #'cddr
       for value in (cdr keys/values) by #'cddr
       do (setf (gethash key newhash) value))
    newhash))

;; Section 12.1 has an example usage of this, including how
;; *EXPORT-TAGS* should be formatted.
(defun import-tags (package-designator &rest tags)
  "Helps emulate Perl's EXPORT_TAGS functionality, which has no
equivalent in standard CL."
  (let* ((current-package *package*)
         (*package* (find-package package-designator))
         ;; Otherwise we'll find the *export-tags* from the "calling"
         ;; package.
         (export-tags (symbol-value (find-symbol "*EXPORT-TAGS*" *package*))))
    (dolist (tag tags)
      (import (cadr (assoc tag export-tags))
              current-package))))

;; Like Perl's keys function.
(defun hash-keys (hash)
  (loop for k being the hash-keys of hash collect k))

;;;; @@SKIP@@ Common Lisp code makes use of the following for
;;;; @@SKIP@@ package/loading: 
;;;; @@SKIP@@  (require :PACKAGENAME)

;;;; @@SKIP@@ SBCL code makes use of the following for package /
;;;; @@SKIP@@ library loading:
;;;; @@SKIP@@  (asdf:operate 'asdf:load-op :date-calc) ; load the package
;;;; @@SKIP@@  (use-package 'date-calc)                ; import the symbols
;;;; @@SKIP@@  (load "time.lisp")              ; replace with your location of the pdl library
;;;; @@SKIP@@  (use-package 'CyberTiggyr-Time) ; for printing times in various formats

;;;; @@SKIP@@ Packages / libraries used include:
;;;; @@SKIP@@  http://cybertiggyr.com/gene/pdl/
;;;; @@SKIP@@  http://www.cliki.net/asdf
;;;; @@SKIP@@  http://www.cliki.net/cl-interpol
;;;; @@SKIP@@  http://www.cliki.net/cl-ppcre
;;;; @@SKIP@@  http://www.cliki.net/date-calc
;;;; @@SKIP@@  http://www.cliki.net/iterate

;;; @@PLEAC@@_1.0
;;;-----------------------------
(setf string "\\n")                     ; two characters, \ and an n
(setf string "John 'Maddog' Orwant")    ; literal single quotes
;;;-----------------------------
;; newlines may be inserted literally
(setf string "                          
")                                      ; a "newline" character
;; or by creating a string explicitly
(setf string (make-string 1 :initial-element #\Newline))
;; or by using format with a nil output stream
(setf string (format nil "~%"))
(setf string "John \"Maddog\" Orwant")  ; literal double quotes
;;;-----------------------------
(setf string "John 'Maddog' Orwant")  ; literal single quotes
;;;-----------------------------
(setf string "
This is a multiline string, terminated by a
double quotation mark.
")
;;;-----------------------------
;; There are no CL equivalents to Perl's other ways of quoting
;; strings (q//, etc).
;;;-----------------------------

;;; @@PLEAC@@_1.1
;;;-----------------------------
;; assign a substring to a variable
(setf value (subseq string offset (+ offset count)))
(setf value (subseq string offset))

;; edit a substring
(setf string (concatenate 'string 
                          (subseq string 0 offset) 
                          newstring
                          (subseq string (+ offset count))))
(setf string (concatenate 'string 
                          (subseq string 0 offset) 
                          newtail))
;;;-----------------------------
;; get a 5-byte string, skip 3, then grab 2 8-byte strings, then the
;; rest
(setf leading (subseq data 5)
      s1 (subseq data 8 8)
      s2 (subseq data 16 8)
      trailing (subseq data 24))

;; split at five byte boundries
(let ((length (length string)))
  (loop for idx from 0 upto length by 5
     collect (subseq string idx (min length (+ idx 5)))))

;; chop string into individual characters
(loop for idx from 0 upto (1- (length string))
      collect (char string idx))
;;;-----------------------------
(defparameter *string* "This is what you have")
;;;             +012345678901234567890  Indexing forwards  (left to right)
;;;              109876543210987654321- Indexing backwards (right to left)

(let ((first (subseq *string* 0 1))                        ; "T"
      (start (subseq *string* 5 7))                        ; "is"
      (rest (subseq *string* 13))                       ; "you have"
      (last (subseq *string* (+ (length *string*) -1))) ; "e"
      (end (subseq *string* (+ (length *string*) -4)))   ; "have"
      (piece (subseq *string*
                     (+ (length *string*) -8)
                     (+ (length *string*) -8 3)))) ; "you"
  (list first start rest last end piece))
;; ("T" "is" "you have" "e" "have" "you")
;;;-----------------------------
(defparameter *string* "This is what you have")
(print *string*)
;; This is what you have

;; Change "is" to "wasn't"
(setf *string* (concatenate 'string
                            (subseq *string* 0 5)
                            "wasn't"
                            (subseq *string* (+ 5 2))))
;; This wasn't what you have

;; Replace last 12 characters
(setf *string* (concatenate 'string
                            (subseq *string* 0 (+ (length *string*) -12))
                            "ondrous"))
;; This wasn't wondrous

;; Delete first character
(setf *string* (subseq *string* 1))
;; his wasn't wondrous

;; Delete last 10 characters
(setf *string* (subseq *string* 0 (+ (length *string*) -10)))
;; his wasn'
;;;-----------------------------
;; you can test substrings with the :start and :end keyword parameters
;; of CL-PPCRE:SCAN
(when (scan "pattern" *string* :start (- (length *string*) 10))
  (format t "Pattern matches in last 10 characters~%"))

;; substitute "at" for "is", restricted to first five characters
(concatenate 'string
              (regex-replace "is" *string* "at"
                             :start 0
                             :end (min (length *string*) 5))
              (when (> (length *string*) 5)
                (subseq *string* 5)))
;;;-----------------------------
;; exchange the first and last letters in a string
(let ((a "make a hat"))
  ;; ROTATEF is CL's general-purpose swap macro
  (rotatef (char a 0) (char a (1- (length a))))
  (princ a))
;; take a ham
;;;-----------------------------
;; extract column with SUBSEQ
(let* ((a "To be or not to be")
       (b (subseq a 6 12))) ; skip 6, grab 6
  (format t "~A~%" b)
;; or not

  ;; forward 6, grab 2; backward 5, grab 2
  (destructuring-bind (b c) `(,(subseq a 6 8) ,(subseq a 3 5))
    (format t "~A~%~A~%" b c)))
;; or
;; be
;;;-----------------------------
(defun cut2fmt (&rest positions)
  "Useless in CL, which lacks Perl's unpack(); here for completeness."
  (let ((template "")
        (lastpos 1))
    (dolist (place positions)
      (setf template (format nil "~AA~D " template (- place lastpos)))
      (setf lastpos place))
    (setf template (concatenate 'string template "A*"))
    template))
(let ((fmt (cut2fmt 8 14 20 26 30)))
  (format t "~A~%" fmt))
;; A7 A6 A6 A6 A4 A*
;;;-----------------------------

;;; @@PLEAC@@_1.2
;;;-----------------------------
;; use b if b is true, else c
(setf a (or b c))

;; set x to y unless x is already true
(unless x (setf x y))
;;;-----------------------------
;; use B if B is defined, otherwise C
(setf a (if (boundp 'b) b c))
;;;-----------------------------
(setf foo (or bar "DEFAULT VALUE"))
;;;-----------------------------
#+sbcl 
(defparameter ARGV (copy-seq (cdr *posix-argv*)) "Arguments from shell, Perl style")

(setf dir (or (pop ARGV) "/tmp"))
;;;-----------------------------
(setf dir (or (nth 0 ARGV) "/tmp"))
;;;-----------------------------
(setf dir (if (plusp (length ARGV)) (pop ARGV) "/tmp"))
;;;-----------------------------
(setf dir (if (plusp (length ARGV)) (nth 0 ARGV) "/tmp"))
;;;-----------------------------
(setf count (make-hash-table))
(incf (gethash (or shell "/bin/sh") count 0))
;;;-----------------------------
;; find the user name on Unix systems
(setf user (or (posix-getenv "USER")
               (posix-getenv "LOGNAME")
               #+sbcl
               (let ((uid (sb-posix:getuid)))
                 (or (sb-posix:passwd-name (sb-posix:getpwuid uid))
                     (format nil "Unknown uid number ~a" uid)))
               #-sbcl
               "Unknown uid"))
;;;-----------------------------
(setf starting-point (or starting-point "Greenwich"))
;;;-----------------------------
;; Perl array-to-array assignment copies the array, hence the need for
;; COPY-SEQ below.
(unless (plusp (length a))
  (setf a (copy-seq b)))                ; copy only if empty
(setf a (copy-seq (if (plusp (length b))
                      b
                      c)))              ; assign b if nonempty, else c
;;;-----------------------------

;;; @@PLEAC@@_1.3
;;;-----------------------------
;; There are several ways to swap variables in CL.  ROTATEF is usually
;; the simplest choice.
(rotatef VAR1 VAR2)
(psetq VAR1 VAR2 VAR2 VAR1)
(multiple-value-setq (VAR1 VAR2) 
  (values VAR2 VAR1))
;;;-----------------------------
(setf temp a
      a b
      b temp)
;;;-----------------------------
(let ((a "alpha")
      (b "omega"))
  (rotatef a b))           ; the first shall be last -- and versa vice
;;;-----------------------------
(destructuring-bind (alpha beta production)
    ;; In CL one would normally use symbols here:
    ;; '(January March August)
    '("January" "March" "August")
  ;; move beta       to alpha,
  ;; move production to beta,
  ;; move alpha      to production
  (rotatef alpha beta production))
;;;-----------------------------

;;; @@PLEAC@@_1.4
;;-----------------------------
(setq num (char-code char))
(setq char (code-char num))
;;-----------------------------
(setq char (format nil "~c" (code-char num)))
(format t "Number ~d is character ~c~%" num (code-char num))
;; Number 101 is character e
;;;-----------------------------
(setq ASCII (map 'list #'char-code string))
(setq string (map 'string #'code-char ASCII))
;;-----------------------------
(setq ascii-value (char-code #\e))             ; now 101
(setq character (code-char 101))               ; now #\e
;;-----------------------------
(format t "Number ~D is character ~C~%" 101 (code-char 101))
;;-----------------------------
(let ((ascii-character-numbers (map 'list #'char-code "sample")))
  (format t "~{~A~^ ~}~%" ascii-character-numbers)
;; 115 97 109 112 108 101
  (setf word (map 'string #'code-char ascii-character-numbers))
  (setf word (map 'string #'code-char #(115 97 109 112 108 101))) ; same
  (format t "~A~%" word))
;; sample
;;;-----------------------------
(let* ((hal "HAL")
       (ibm (map 'string
                 (lambda (char)
                   (code-char (1+ (char-code char)))) ; add one to each ASCII value
                 hal)))
  (format t "~A~%" ibm))                ; prints "IBM"
;;;-----------------------------

;;; @@PLEAC@@_1.5
;;;-----------------------------
(setq array (map 'list #'string string))

(setq array (loop for char across string
               collect (char-code char)))
;;;-----------------------------
(do-matches-as-strings (match "(.)" string)
  ;; do something with MATCH
  )
;;;-----------------------------
(let ((seen (make-hash-table))
      (string "an apple a day"))
  (loop for char across string do
       (incf (gethash char seen 0)))
  (let ((chars (loop for char being each hash-key of seen
                  collect char)))
    (format t "unique chars are: ~{~C~}~%" 
            (sort chars '< :key 'char-code))))
;; unique chars are:  adelnpy
;;;-----------------------------
(let ((string "an apple a day"))
  (format t "sum is ~D~%"
          (reduce #'+ string :key #'char-code)))
(let ((string "an apple a day"))
  (format t "sum is ~D~%"
          (loop for char across string
                summing (char-code char))))
;;;-----------------------------
(let ((string "an apple a day"))
  (format t "sum is ~D~%"
          (loop for char across string
             sum (char-code char))))
;; prints "1248" if string was "an apple a day"
;;;-----------------------------
;; There's no equivalent to Perl's unpack(), this is about as close as
;; you can get.
(setf sum (loop for char across string
             sum (char-code char)))
;;;-----------------------------
;; In CL it makes more sense to call this function from the REPL than
;; it does to put it into a separate script.  E.g.,
;;  > (sum "/tmp/xyz" "~/foo/bar.txt")
(defun sum (&rest files)
  (let ((sum 0))
    (dolist (filename files)
      (with-open-file (file filename :element-type 'unsigned-byte)
        (do ((b (read-byte file nil :eof)
                (read-byte file nil :eof)))
            ((eql b :eof))
          (incf sum b))))
    (let ((r (+ (mod sum (expt 2 16))
                (truncate (/ (mod sum (expt 2 32))
                             (expt 2 16))))))
      (+ (mod r (expt 2 16))
         (truncate (/ r (expt 2 16)))))))
;;;-----------------------------
;;CL-USER> (sum "/mach.sym")
;;24298
;;;-----------------------------
;;%cksum -o 2 /mach.sym
;;24298 1203 /mach.sym
;;;-----------------------------
(defun slowcat (number-or-filename &rest more-files)
  "The first argument can be a number of seconds to sleep between
characters, otherwise it should be a file name."
  (let ((delay (if (numberp number-or-filename) number-or-filename 1))
        (files (if (numberp number-or-filename) more-files (cons number-or-filename more-files))))
    (dolist (filename files)
      (with-open-file (file filename)
        (do ((c (read-char file nil :eof)
                (read-char file nil :eof)))
            ((eql c :eof))
          (format t "~C" c)
          (finish-output)
          (sleep (* delay 0.005)))))))

;;; @@PLEAC@@_1.6
;;;-----------------------------
(setq revbytes (reverse string))
;;;-----------------------------
(setq revwords (format nil "~{~A~^ ~}"
                       (reverse (split " " string))))
(setq revwords (reverse
                (do* ((stringstream (make-string-input-stream string))
                      (result nil (cons next result))
                      (next (read stringstream nil 'eos)
                            (read stringstream nil 'eos)))
                     ((equal next 'eos)
                      (reverse result)))))
;;;-----------------------------
(setq gnirts (reverse string))          ; reverse letters in string

(setq sdrow (reverse words))            ; reverse elements in words

(setq confused (reverse (apply #'concatenate 'string words))) ; reverse letters in join("", @words)
;;;-----------------------------
(setq string "Yoda said, \"can you see this?\"")
(setq allwords (split " " string))
(setq revwords (format nil "~{~A~^ ~}" (reverse allwords)))
(format t "~A~%" revwords)
;this?" see you "can said, Yoda
;;;-----------------------------
(setq revwords (format nil "~{~A~^ ~}" (reverse (split " " string))))
;;;-----------------------------
(setq revwords (apply #'concatenate 'string
                      (split "(\\s+)" string :with-registers-p t)))
;;;-----------------------------
(defun palindrome-p (word)
  (string= word (reverse word)))
(palindrome-p "reviver")
;; T
;;;-----------------------------
(with-open-file (inf "/usr/share/dict/words")
  (loop for word = (read-line inf nil nil)
        while word
        when (and (string= word (reverse word)) 
                  (> (length word) 5))
        do (format t "~a~%" word)))
;; deedeed
;; degged
;; hallah
;; kakkak
;; murdrum
;; redder
;; repaper
;; retter
;; reviver
;; rotator
;; sooloos
;; tebbet
;; terret
;;;-----------------------------

;;; @@PLEAC@@_1.7
;;;-----------------------------

;;;-----------------------------
(defun tab-expand (string &optional (tabstop 8))
  (flet ((needed-spaces (target-string start end match-start match-end reg-starts reg-ends)
           (declare (ignore target-string start end reg-starts reg-ends))
           (make-string (- (* (- match-end match-start) tabstop)
                           (mod match-start tabstop))
                        :initial-element #\Space)))
    (regex-replace-all "\\t+" string #'needed-spaces)))

(defun tab-unexpand (string &optional (tabstop 8))
  (flet ((needed-tabs (target-string start end match-start match-end reg-starts reg-ends)
           (declare (ignore target-string start end reg-starts reg-ends))
           (let ((match-length (- match-end match-start)))
             (concatenate 'string
                          (make-string (floor match-length tabstop)
                                       :initial-element #\Tab)
                          (make-string (mod match-length tabstop)
                                       :initial-element #\Space)))))
    (regex-replace-all "  +" string #'needed-tabs)))
;;;-----------------------------
(loop for line = (read-line *standard-input* nil nil)
      while line do
      (format t "~A~%" (tab-expand line)))
;;;-----------------------------
(loop for line = (read-line *standard-input* nil nil)
      while line do
      (format t "~A~%" (tab-unexpand line)))
;;;-----------------------------

;;; @@PLEAC@@_1.8
;;;-----------------------------

(setf text "You owe $debt to me")
;;;-----------------------------
(defun global-deref (match var-name)
  "Helper function to simulate Perl's string interpolation in
regexps."
  (write-to-string (symbol-value (intern (string-upcase var-name)))))

(setf text (regex-replace-all "\\$(\\w+)" text #'global-deref
                                       :simple-calls t))
;;;-----------------------------
(setq rows 24 cols 80)
(setq text "I am $rows high and $cols long")
(setq text (regex-replace-all "\\$(\\w+)" text
                                       #'global-deref
                                       :simple-calls t))
(format t "~A~%" text)
;; I am 24 high and 80 long
;;;-----------------------------
(setf text "I am 17 years old")
(setf text (regex-replace-all "(\\d+)" text
                                       (lambda (match num-str)
                                         (declare (ignore match))
                                         (write-to-string
                                          (* 2 (parse-integer num-str))))
                                       :simple-calls t))
;;;-----------------------------
(* 2 17)
;;;-----------------------------
;; expand variables in text, but put an error message in
;; if the variable isn't defined
(flet ((deref-with-err (match word)
         (declare (ignore match))
         (let ((word-sym (intern (string-upcase word))))
           (if (boundp word-sym)
               (write-to-string (symbol-value word-sym))
               (format nil "[NO VARIABLE: $~a]" word-sym)))))
  (setf text (regex-replace-all "\\$(\\w+)" text
                                         #'deref-with-err
                                         :simple-calls t)))
;;;-----------------------------


;;; @@PLEAC@@_1.9
;;;-----------------------------
(setf big (string-upcase little))       ; "bo peep" -> "BO PEEP"
(setf little (string-downcase big))     ; "JOHN"    -> "john"
;; Reminder: the following depends on CL-INTERPOL.
(setf big #?"\U$(little)")              ; "bo peep" -> "BO PEEP"
(setf little #?"\L$(big)")              ; "JOHN" -> "john"
;;;-----------------------------
(setf big (string-upcase little :end 1)    ; "bo"      -> "Bo"
(setf little (string-downcase BIG :end 1)) ; "BoPeep"    -> "boPeep"
(setf big #?"\u$(little)")                 ; "bo"      -> "Bo"
(setf little #?"\l$(big)")                 ; "BoPeep"    -> "boPeep"
;;;-----------------------------
(setf beast "dromedary")
;; Capitalize various parts of beast
(setf capit (string-upcase beast :end 1))      ; Dromedary
(setf capit #?"\u\L$(beast)")                  ; (same)
(setf capall (string-upcase beast))            ; DROMEDARY
(setf capall #?"\U$(beast)")                   ; (same)
(setf caprest (string-downcase (string-upcase beast) :end 1)) ; dROMEDARY
(setf caprest #?"\l\U$(beast)")                               ; (same)
;;;-----------------------------
;; capitalize each word's first character, downcase the rest
(setf text "thIS is a loNG liNE")
(format t "~A~%" (string-capitalize text))
;; This Is A Long Line
;;;-----------------------------
;; string= is case-sensitive, string-equal is case-insensitive
(when (string-equal a b)
  (format t "a and b are the same~%"))
;;;----------------------------- 
;; It's considered bad form to perform bitwise operations on character
;; types, and breaks unicode-aware lisps.  Trust the compiler to
;; optimize.
(defun randcase (char)
  (if (< (random 100) 20)
      (char-upcase char)
      (char-downcase char)))
;;;-----------------------------

;;; @@PLEAC@@_1.10
;;;-----------------------------
(setf answer (concatenate 'string var1 (func) var2))
;;;-----------------------------
(setf answer (format nil "STRING ~{~A~} MORE STRING" list-expr))
(setf answer (format nil "STRING ~A MORE STRING" atomic-expr))
;;;-----------------------------
(setf phrase (format nil "I have ~D guanacos." (1+ n)))
(setf phrase #?"I have ${(1+ n)} guanacos.") ; uses CL-INTERPOL
;;;-----------------------------
(format t "I have ~D guanacos.~%" (1+ n))
;;;-----------------------------
(some-func (format nil "What you want is ~{~A~} items"
                   (split ":" rec)))
;;;-----------------------------
(let ((text (format nil "~
To: ~A
From: Your Bank
Cc: ~{~A~^, ~}
Date: ~A (today)

Dear ~A,

Today, you bounced check number ~D to us.
Your account is now closed.

Sincerely,
the management
"
                    naughty
                    (get-manager-list naughty)
                    (let (date-str (with-output-to-string (str)
                                     (run-program "/bin/date" nil
                                                  :output str)))
                      (subseq date-str 0 (1- (length date-str))))
                    naughty
                    (+ 500 (random 100)))))
  (unless (send-mail text target)
    (error "Couldn't send mail")))
;;;-----------------------------

;;; @@PLEAC@@_1.11
;;;-----------------------------
;; all in one
(setf var (regex-replace-all
           (create-scanner #?r"^\s+" :multi-line-mode t)
"your text
goes here
" ""))

;; or with two steps
(setf var "your text
goes here
")
(setf var (regex-replace-all 
           (create-scanner #?r"^\s+" :multi-line-mode t)
           var
           ""))
;;-----------------------------
(setf var (regex-replace-all 
           (create-scanner #?r"^\s+" :multi-line-mode t)
"    The five varieties of camelids
    are the familiar camel, his friends
    the llama and the alpaca, and the
    rather less well-known guanaco
    and vicuña.
" ""))
;;;-----------------------------
(defun fix (string)
  (regex-replace-all
   (create-scanner #?r"^\s+" :multi-line-mode t)
   string
   ""))

(format t "~A~%" (fix "    My stuff goes here
")
;;;-----------------------------
(setf quote 
      (regex-replace-all
       (create-scanner #?r"\s+--")
       (regex-replace-all 
        (create-scanner #?r"^\s+" :multi-line-mode t)
"        ...we will have peace, when you and all your works have
        perished--and the works of your dark master to whom you would
        deliver us. You are a liar, Saruman, and a corrupter of mens
        hearts.  --Theoden in /usr/src/perl/taint.c
"
       "")
       "
--"))
;;-----------------------------
(when *remember-the-main*
  (setf perl-main-C (dequote
"        @@@ int
         @@@ runops() {
         @@@     SAVEI32(runlevel);
         @@@     runlevel++;
         @@@     while ( op = (*op->op_ppaddr)() ) ;
         @@@     TAINT_NOT;
         @@@     return 0;
         @@@ }
"
  ;; add more code here if you want
   )))
;;;-----------------------------
(defparameter *poem* (dequote
"       Now far ahead the Road has gone,
          And I must follow, if I can,
       Pursuing it with eager feet,
          Until it joins some larger way
       Where many paths and errands meet.
          And whither then? I cannot say.
                --Bilbo in /usr/src/perl/pp_ctl.c
"))

(format t "Here's your poem:~%~%~A~%" *poem*)
;;;-----------------------------
(defun dequote (string)
  ;; Can't get multiple values returned thru the OR, hence the use of
  ;; DESTRUCTURING-BIND instead of MULTIPLE-VALUE-BIND
  (destructuring-bind (white leader) ; common whitespace and common leading string
      (or (register-groups-bind ($1 $2)
              (#?r/^\s*(?:([^\w\s]+)(\s*).*\n)(?:\s*\1\2?.*\n)+$/ string)
            (list $2 (quote-meta-chars $1)))
          (list (scan-to-strings #?r"^(\s+)" string) ""))
    (regex-replace-all
     (create-scanner (format nil #?r"^\s*?~a(?:~a)?" leader white) :multi-line-mode t)
     string
     "")))
;;;-----------------------------

;;; @@PLEAC@@_1.12
(defvar *default-line-width* 72)

(defun partition (list max-part-len item-offset-len)
  "Partitions supplied `LIST' into list of item lists where every sublist is of
maximum `MAX-PART-LEN' size. (`ITEM-OFFSET-LEN' is added to every item while
calculating item lengths.)"
  (destructuring-bind (parts part part-len)
      (reduce
       (lambda (accum item)
         (destructuring-bind (parts part part-len) accum
           (let ((item-len (+ (length item) item-offset-len)))
             (if (< max-part-len (+ part-len item-len))
                 (list (cons (nreverse part) parts) (list item) item-len)
                 (list parts (cons item part) (+ part-len item-len))))))
       list :initial-value (list nil nil 0))
    (declare (ignore part-len))
    (nreverse (if part (cons part parts) parts))))

(defun wrap-paragraph (string &optional line-width)
  (with-output-to-string (out)
    (dolist (words
              (partition
               (ppcre:split "\\s+" string)
               (1+ (or line-width *default-line-width*))
               1))
      (format out "~&~{~A~^ ~}" words))))

(defun wrap-paragraphs (string &optional line-width)
  (format nil "~{~A~^~%~%~}"
          (mapcar (lambda (paragraph) (wrap-paragraph paragraph line-width))
                  (ppcre:split "([\\r]?\\n){2,}" string))))

(wrap-paragraphs "foo0 foo1 foo2 foo3 foo5 foo6 foo7 foo8
foo9 bar0

bar1 bar2


bar3 bar4 bar5 bar6
bar7
bar8 bar9 baz0

baz1 baz2 baz3 baz4 baz5 baz6 baz7 baz8 baz9" 14)
; => "foo0 foo1 foo2
;     foo3 foo5 foo6
;     foo7 foo8 foo9
;     bar0
;     
;     bar2 bar1
;     
;     bar3 bar4 bar5
;     bar6 bar7 bar8
;     baz0 bar9
;     
;     baz1 baz2 baz3
;     baz4 baz5 baz6
;     baz9 baz8 baz7"

;;; @@PLEAC@@_1.13
;; backslash
(setf var (regex-replace-all "([CHARLIST])" var "\\\1"))

;; double
(setf var (regex-replace-all "([CHARLIST])" var "\\1\\1"))
;;;-----------------------------
(setf string (regex-replace-all "%" string "%%"))
;;;-----------------------------
(setf string "Mom said, \"Don't do that.\"")
(setf string (regex-replace-all "(['\"])" string #?r"\\\1"))
;;;-----------------------------
(setf string "Mom said, \"Don't do that.\"")
(setf string (regex-replace-all "(['\"])" string #?r"\1\1"))
;;;-----------------------------
(setf string (regex-replace-all #?r"([^A-Z])" string #?r"\\\1"))
;;;-----------------------------
(setf string #?r"this \Qis a test!\E")
(setf string "this is\\ a\\ test\\!")
(setf string (concatenate 'string "this " (quote-meta-chars "is a test!")))
;;;-----------------------------

;;; @@PLEAC@@_1.14
;;;-----------------------------
(setf string (regex-replace #?r/^\s+/ string "")) 
(setf string (regex-replace #?r/\s+$/ string ""))
;;;-----------------------------

;;; The closest thing to Perl's wantarray is CL's ability to return
;;; multiple values from a function.  Unless the caller uses
;;; MULTIPLE-VALUE-BIND (or, in this case, MULTIPLE-VALUE-LIST), they
;;; will only "see" the first value.  Note also that, normally, you'd
;;; use CL's built-in STRING-TRIM function for this.
(defun trim (&rest strings)
  (values-list 
   (loop for string in strings
      collect (regex-replace #?r/^\s+/ 
                                      (regex-replace #?r/\s+$/ string "")
                                      ""))))

(setf string (trim string))
(setf many (multiple-value-list (apply 'trim many)))
;;;-----------------------------
;; print what's typed, but surrounded by >< symbols
(loop
   (let ((line (read-line)))
     (chomp line)
     (format t #?">$(line)<~%")))
;;;-----------------------------

;;; @@PLEAC@@_1.15
;;;-----------------------------
(defun parse-csv (text)
  (let (fields)
    (cl-ppcre:do-register-groups (quoted unquoted)
        ("\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\",?|([^,]+),?|," text)
      (push (or quoted unquoted) fields))
    (nreverse fields)))
;;;-----------------------------
;; CL has no obvious equivalent to Text::ParseWords
;;;-----------------------------
(defparameter *line* "XYZZY,\"\",\"O'Reilly, Inc\",\"Wall, Larry\",\"a \\\"glug\\\" bit,\",5,\"Error, Core Dumped\"")
(let ((fields (parse-csv *line*)))
  (loop
     for i below (length fields)
     do (format t "~D : ~A~%" i (elt fields i))))
;;0 : XYZZY
;;1 : 
;;2 : O'Reilly, Inc
;;3 : Wall, Larry
;;4 : a \"glug\" bit,
;;5 : 5
;;6 : Error, Core Dumped
;;;-----------------------------


;;; @@PLEAC@@_1.16
(defun system-users ()
  (with-open-file (in "/etc/passwd")
    (loop for line = (read-line in nil nil)
          while line collect (ppcre:split ":" line))))

(defun soundex (string)
  (soundex:soundex
   (with-output-to-string (out)
     (loop for char across string
           when (alphanumericp char)
             do (format out "~C" char)))))

(defun lookup-user (user)
  (let ((user-code (soundex user)))
  (dolist (user-spec (system-users))
    (dolist (user-name (ppcre:split "\\s+" (elt user-spec 4)))
      (unless (mismatch user-code (soundex user-name))
        (format t "User: ~S. (Matched Token: ~S.)~%"
                (elt user-spec 0) user-name))))))

(defun prompt-read (prompt)
  (format *query-io* "~A: " prompt)
  (force-output *query-io*)
  (read-line *query-io*))

(defun prompt-lookup-user ()
  (lookup-user (prompt-read "Lookup user")))

; TEST> (prompt-lookup-user)
; Lookup user: volkan
; User: "vy". (Matched Token: "Volkan".)


;;; @@PLEAC@@_1.17
(defvar *replacements*
  '(("analysed"        . "analyzed")
    ("built-in"        . "builtin")
    ("chastized"       . "chastised")
    ("commandline"     . "command-line")
    ("de-allocate"     . "deallocate")
    ("dropin"          . "drop-in")
    ("hardcode"        . "hard-code")
    ("meta-data"       . "metadata")
    ("multicharacter"  . "multi-character")
    ("multiway"        . "multi-way")
    ("non-empty"       . "nonempty")
    ("non-profit"      . "nonprofit")
    ("non-trappable"   . "nontrappable")
    ("pre-define"      . "predefine")
    ("preextend"       . "pre-extend")
    ("re-compiling"    . "recompiling")
    ("reenter"         . "re-enter")
    ("turnkey"         . "turn-key")))

(let ((scanner
       (ppcre:create-scanner
        (format nil "(~{~A~^|~})" (mapcar #'car *replacements*)))))
  (defun apply-replacements (string)
    (ppcre:regex-replace-all
     scanner string
     (lambda (target-string start end match-start match-end reg-starts reg-ends)
       (declare (ignore start end reg-starts reg-ends))
       (cdr (find (make-array (- match-end match-start)
                              :element-type (array-element-type target-string)
                              :displaced-to target-string
                              :displaced-index-offset match-start)
                  *replacements* :test #'string-equal :key #'car))))))

(defun replace-stream (in out)
  (loop for line = (read-line in nil nil)
        while line do (format out "~&~A" (apply-replacements line))))

(with-input-from-string
    (stream
     (format nil "~{~&~A~}"
             '("If I have analysed these built-in"
               "results correctly in a chastized manner"
               "from commandline")))
  (with-output-to-string (out)
    (replace-stream stream out)))
; => "If I have analyzed these builtin
;     results correctly in a chastised manner
;     from command-line"


;;; @@PLEAC@@_1.18
(defvar *ps-fields*
  '((flags   . integer)
    (uid     . integer)
    (pid     . integer)
    (ppid    . integer)
    (pri     . integer)
    (nice    . integer)
    (size    . integer)
    (rss     . integer)
    (wchan   . string)
    (stat    . string)
    (tty     . string)
    (time    . string)
    (command . string)))

(defun format-ps-fields (fields)
  (mapcar
   (lambda (field-type field)
     (ecase field-type
       (integer (parse-integer field :junk-allowed t))
       (string field)))
   (mapcar #'cdr *ps-fields*)
   fields))

(defun ps ()
  (with-input-from-string
      (in
       (trivial-shell:shell-command
        (format nil "ps -o ~{~(~A~)~^,~}" (mapcar #'car *ps-fields*))))
    (let ((header (read-line in nil nil))
          (lines (loop for line = (read-line in nil nil)
                       while line collect line)))
      (values (mapcar #'format-ps-fields
                      (mapcar
                       (let ((limit (length *ps-fields*)))
                         (lambda (line) (ppcre:split "\\s+" line :limit limit)))
                       lines))
              header lines))))

(defmacro ps-grep (expr)
  (alexandria:with-unique-names (header-printed-p entries entry header lines line)
    `(let (,header-printed-p)
       (multiple-value-bind (,entries ,header ,lines) (ps)
         (loop for ,entry in ,entries
               for ,line in ,lines
               when (destructuring-bind ,(mapcar #'car *ps-fields*) ,entry
                      (declare (ignorable ,@(mapcar #'car *ps-fields*)))
                      ,expr)
                 do (unless ,header-printed-p
                      (format t "~A~%" ,header)
                      (setq ,header-printed-p t))
                    (format t "~A~%" ,line))))))

(ps-grep (and (< 2220 rss) (= flags 1)))
; => F   UID   PID  PPID PRI  NI    SZ   RSS WCHAN  STAT TT           TIME COMMAND
;    1  1000  2931     1  19   0   828  2644 -      Ss   ?        00:00:03 /usr/bin/fetchmail --daemon 600 --logfile /home/vy/.fetchmail.log
;    1  1000  3106  3104  19   0  1700  2360 -      Ss   ?        00:00:09 SCREEN -U


;;; @@PLEAC@@_2.1
;;;-----------------------------
(if (every #'digit-char-p string)
    (progn
      ;; is a number
      )
    (progn
      ;; is not
      ))
;;;-----------------------------
;;; Strings and numbers are separate data types in CL. These tests
;;; check whether a string represents a number
(unless (every #'digit-char-p string)
  (format *error-output* "string has nondigits"))
(unless (scan "^\\d+$" string) ; rejects -3
  (format *error-output* "not a natural number"))
(unless (scan "^-?\\d+$" string) ; rejects +3
  (format *error-output* "not an integer"))
(unless (scan "^[+-]?\\d+$" string)
  (format *error-output* "not an integer"))
(unless (scan "^-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)$" string)
  (format *error-output* "not an integer"))
(unless (scan "^([+-]?)(?=\\d|\\.\\d)\\d*(\\.\\d*)?([Ee]([+-]?\\d+))?$"
                       string)
  (format *error-output* "not a C float"))
;;;-----------------------------
(defun getnum (string)
  "This function is not safe to call on untrusted input."
  (with-input-from-string 
      (is (regex-replace #?"\s+$" 
                                  (regex-replace #?"^\s+" string "") ""))
    (let ((num (read is nil nil nil)))
      (and
       ;; Make sure there's no junk following the number
       (eql (read-char is nil :eof nil) :eof)
       (numberp num)
       num))))

(defun is-numeric (string)
  (not (null (getnum string))))
;;;-----------------------------

;;; @@PLEAC@@_2.2
;;;-----------------------------
(defun equal-to-accuracy (number1 number2 dp)
  "Return non-nil if NUMBER1 and NUMBER2 are equal to DP number of
decimal places."
  (let* ((difference (abs (- number1 number2)))
         (delta (expt 10 (- dp))))
    (< difference delta)))
;;;-----------------------------
(let* ((wage 536)                       ; $5.36/hour
       (week (* 40 wage)))              ; $214.40
  (format t "One week's wage is: $~,2F~%" (/ week 100)))
;;One week's wage is: $214.40
;;;-----------------------------

;;; @@PLEAC@@_2.3
;;;-----------------------------
(setf rounded (format nil "~FORMATF" unrounded))
;;;-----------------------------
(let* ((a 0.255)
       (b (/ (fround a 0.01) 100)))
  (format t "Unrounded: ~F~%Rounded: ~,2F~%" a b))
;;Unrounded: 0.255
;;Rounded: 0.26
;;;-----------------------------
(progn
  (format t "~&number~Tint~Tfloor~Tceil~%")
  (let ((as '(3.3 3.5 3.7 -3.3)))
    (dolist (a as)
      (format t "~@{~4,1F~^~T~}~%"
              a
              (ftruncate a)
              (ffloor a)
              (fceiling a)))))
;;number int floor ceil
;; 3.3  3.0  3.0  4.0
;; 3.5  3.0  3.0  4.0
;; 3.7  3.0  3.0  4.0
;;-3.3 -3.0 -4.0 -3.0
;;;-----------------------------

;;; @@PLEAC@@_2.4
;;;-----------------------------
(defun dec2bin (dec)
  (format nil "~2R" dec))
;;;-----------------------------
(defun bin2dec (bin)
  "BIN is a string containing only #\1 and #\0 characters.  Returns
its integer equivalent."
  (read (make-string-input-stream (concatenate 'string "#b" bin)) 
        t nil nil))
;;;-----------------------------
(setf num (bin2dec "0110110"))          ; $num is 54
(setf binstr (dec2bin 54))              ; binstr is "110110"
;;;-----------------------------

;;; @PLEAC@@_2.5
(defun seq (start end)
  (loop for i from start to end collect i))

(format t "Infancy is: ~{~A~^ ~}~%" (seq 0 2))
(format t "Toddling is: ~{~A~^ ~}~%" (seq 3 4))
(format t "Childhood is: ~{~A~^ ~}~%" (seq 5 12))
; => Infancy is: 0 1 2
;    Toddling is: 3 4
;    Childhood is: 5 6 7 8 9 10 11 12


;;; @@PLEAC@@_2.6
;;; CL has a built in FORMAT directive (used below) to print out
;;; numbers as roman numerals, but doesn't have a built-in mechanism
;;; to convert back.  Here are some rough CL equivalents of Perl's
;;; Roman package.

(defun romanchar->num (x) 
  (case (char-downcase x)
    (#\m 1000)
    (#\d 500)
    (#\c 100)
    (#\l 50)
    (#\x 10)
    (#\v 5)
    (#\i 1)
    (t 0)))

(defun isroman (string)
  (every #'(lambda (c)
             (plusp (romanchar->num c)))
         string))

(defun arabic (string)
  (let ((digits (map 'list #'romanchar->num string)))
    (reduce #'+ (mapcar #'(lambda (digit next-digit)
                            (if (< digit next-digit) 
                                (- digit) 
                                digit))
                        digits
                        (append (rest digits) '(0))))))

(setf roman (format nil "~@R" arabic))  ; convert to roman numerals
(when (isroman roman) (setf arabic (arabic roman))) ; convert from roman numerals 
;;;-----------------------------
(setf roman-fifteen (format nil "~@R" 15))
(format t "Roman for fifteen is ~A~%" roman-fifteen)
(setf arabic-fifteen (arabic roman-fifteen))
(format t "Converted back, ~A is ~A~%" roman-fifteen arabic-fifteen)
;;Roman for fifteen is XV
;;Converted back, XV is 15
;;;-----------------------------

;;; @@PLEAC@@_2.7
;;;-----------------------------
(setf random (+ (random (+ y (- x) 1)) x)
;;;-----------------------------
(setf random (+ (random 51) 25))
(format t "~A~%" random)
;; If you wanted to use floats...
(+ (random 51.0) 25.0)
;;;-----------------------------
(setf elt (aref array (random (length array))))
;;;-----------------------------
(setf chars "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345789!@$%^&*")
(setf password (coerce (loop repeat 8 collect (aref chars (random (length chars)))) 'string))
;;;-----------------------------

;;; @@PLEAC@@_2.8
;;;-----------------------------
;; CL intentionally does not have an equivalent of Perl's srand(); you
;; can call MAKE-RANDOM-STATE but its inner workings are not exposed.
(setf *random-state* (make-random-state t))
;;;-----------------------------

;;; @@PLEAC@@_2.9
;;;-----------------------------
;; In CL, RANDOM is supposed to return a truly random,
;; uniformly-distributed value.
(setf random (random))
;;;-----------------------------

;;; @@PLEAC@@_2.10
;;;-----------------------------
;; Note: (random 1.0) is the same as calling Perl's rand() with no
;; arguments.
(defun gaussian-rand ()
  (do* ((u1 (1- (* 2 (random 1.0)))
            (1- (* 2 (random 1.0))))
        (u2 (1- (* 2 (random 1.0)))
            (1- (* 2 (random 1.0))))
        (w (+ (* u1 u1) (* u2 u2))
           (+ (* u1 u1) (* u2 u2))))
       ((< w 1.0)
        (let* ((w2 (sqrt (/ (* -2 (log w)) w)))
               (g2 (* u1 w2))
               (g1 (* u2 w2)))
          ;; No need for wantarray in CL because functions can return
          ;; multiple values.
          (values g1 g2)))))
;;;-----------------------------
(defun weight-to-dist (weights)
  "Takes a hash mapping key to weight and returns a hash mapping key
to probability.  WEIGHTS is an alist."
  (let ((dist (make-hash-table))
        (total (loop for (key . ignored-value) in weights sum key)))
    (loop for (key . weight) in weights
         do (setf (gethash key dist) (/ weight total)))
    dist))

(defun weighted-rand (dist)
  "Takes a hash mapping key to probability, and returns the
corresponding element."
  (loop
     for rand = (random 1.0)
     do
     (loop for key being the hash-keys of dist using (hash-value weight)
        do
        (decf rand weight)
        (when (minusp rand)
          (return-from weighted-rand key)))))
;;;-----------------------------
;; gaussian_rand as above
(let* ((mean 25)
       (sdev 2)
       (salary (+ (* (gaussian-rand) sdev) mean)))
  (format t "You have been hired at $~,2F~%" salary))
;;;-----------------------------

;;; @@PLEAC@@_2.11
;;;-----------------------------
(defun deg2rad (degrees) 
  (* (/ degrees 180) pi))

(defun rad2deg (radians) 
  (* (/ radians pi) 180))
;;;-----------------------------
(setf radians (deg2rad degrees))
(setf degrees (rad2deg radians))
;;;-----------------------------
;; deg2rad and rad2deg defined either as above
(defun degree-sine (degrees)
  (let ((radians (deg2rad degrees)))
    (sin radians)))
;;;-----------------------------

;;; @@PLEAC@@_2.12
;;;-----------------------------
;; tangent is built in to CL
(tan theta)
;;;-----------------------------
(setf y (acos 3.7))
;;;-----------------------------
(setf y (tan (/ pi 2)))
;;;-----------------------------

;;; @@PLEAC@@_2.13
;;;-----------------------------
(setf log-e (log value))
;;;-----------------------------
(setf log-10 (log value 10))
(setf log-base-whatever (log value base))
;;;-----------------------------
;; In CL, don't need custom log_base function as LOG already does it
;;;-----------------------------
(setf answer (log 10000 10))
(format t "log10(10,000) = ~D~%" answer)
;; log10(10,000) = 4.0
;;;-----------------------------
(format t "log2(1024) = ~A~%" (log 1024 2))
;; log2(1024) = 10.0
;;;-----------------------------

;;; @@PLEAC@@_2.14
;;;-----------------------------
;;;-----------------------------
(defun mmult (m1 m2)
  (check-type m1 (array * (* *)))
  (check-type m2 (array * (* *)))
  (let* ((m1-rows (array-dimension m1 0))
         (m1-columns (array-dimension m1 1))
         (m2-rows (array-dimension m2 0))
         (m2-columns (array-dimension m2 1)))
    (unless (= m1-columns m2-rows)
      (error 'simple-type-error
             :format-control "IndexError: matrices don't match: ~A != ~A"
             :format-args (list m1-columns m2-rows)))
    (let ((result (make-array (list m1-rows m2-columns))))
      (dotimes (i m1-rows)
        (dotimes (j m2-columns)
          (setf (aref result i j)
                (loop for k from 0 below m1-columns
                      summing (* (aref m1 i k) (aref m2 k j))))))
      result)))

(defun range (n)
  (loop for i from 0 below n collect i))

;; This isn't really necessary in CL, but is here to match the Perl
;; function.
(defun veclen (vector)
  (check-type vector simple-vector)
  (array-dimension vector 0))

;; This isn't really necessary in CL, but is here to match the Perl
;; function.
(defun matdim (matrix)
  (values (array-dimension matrix 0)
          (array-dimension matrix 1)))
;;;-----------------------------
;; Can't find an obvious equivalent to PDL (yet)
;;;-----------------------------
(setf x #2a((3 2 3)
            (5 9 8))
      y #2a((4 7)
            (9 3)
            (8 1)))

(setf z (mmult x y))
;;;-----------------------------

;;; @@PLEAC@@_2.15
;;;-----------------------------
;; Complex numbers are built in to CL so there is no need to compute
;; their product by hand.
;;;-----------------------------
;; c = a * b using built-in CL functionality
(setf c (* a b))
;;;-----------------------------
;; Again, no need to do complex number stuff by hand.
;;;-----------------------------
(setf a #c(3 5))
(setf b #c(2 -2))
(setf c (* a b))
(format t "c = ~D+~Di~%" (realpart c) (imagpart c))
;; c = 16+4i
;;;-----------------------------
(setf c (* #c(3 5) #c(2 -2)))
(setf d #c(3 4))
(let ((sqrt-d (sqrt d)))
  (format t "sqrt(3+4i) = ~D+~Di~%" (realpart sqrt-d) (imagpart sqrt-d)))
;; sqrt(3+4i) = 2.0+1.0i
;;;-----------------------------

;;; @@PLEAC@@_2.16
;;;-----------------------------
(defun hex (string)
  (parse-integer string 
                 :radix 16 
                 :start 2))             ; PARSE-INTEGER dislikes "0x"

(defun oct (string)
  (parse-integer string :radix 8))

(setf number (hex hexadecimal))
(setf number (oct octal))
;;;-----------------------------
(format t "Gimme a number in decimal, octal, or hex: ")
(setf num (read-line))
(when num
  (let ((num (chomp num)))
    (format t "~D ~:*~8R ~:*~X"
            (cond
              ((scan "^0x" num) (hex num))
              ((scan "^0" num) (oct num))
              (t (parse-integer num))))))
;;;-----------------------------
(format t "Enter file permission in octal: ")
(setf permissions (read-line))
(unless permissions (error "Exiting..."))
(let ((permissions (chomp permissions)))
  (format t "The decimal value is ~A~%" (oct permissions)))
;;;-----------------------------

;;; @@PLEAC@@_2.17
(defun comma-separated (input &optional (n-digits 3))
  (coerce
   (first
    (reduce
     (lambda (char accum)
       (format *trace-output* "~S, ~S~%" accum char)
       (destructuring-bind (chars pos) accum
         (list
          (if (and (not (zerop pos)) (zerop (mod pos n-digits)))
              (cons char (cons #\, chars))
              (cons char chars))
          (1+ pos))))
     (coerce (format nil "~A" input) 'list)
     :initial-value (list nil 0)
     :from-end t))
   'string))

; (comma-separated 1234567890)   => "1,234,567,890"
; (comma-separated 1234567890 2) => "12,34,56,78,90"


;;; @@PLEAC@@_2.18
;;; There isn't a  Lingua::EN::Inflect like F/OSS Common Lisp  library I know of
;;; (as of date 2009-03-09). Below is a quick & dirty implementation of its Perl
;;; equivalent.  On  the other hand, one  might be interested  in ~P (pluralize)
;;; directive of  the FORMAT  command and  its modifiers for  a quick  and cheap
;;; solution.

(setq *pluralization-regexps*
  '(("ss$"       . "sses")
    ("([psc]h)$" . "\\1es")
    ("z$"        . "zes")
    ("ff$"       . "ffs")
    ("f$"        . "ves")
    ("ey$"       . "eys")
    ("y$"        . "ies")
    ("ix$"       . "ices")
    ("([sx])$"   . "\\1es")
    ("$"         . "s")))

(defun pluralize (noun)
  (dolist (regexp *pluralization-regexps*)
    (multiple-value-bind (result foundp)
        (ppcre:regex-replace (car regexp) noun (cdr regexp))
      (when foundp (return result)))))

(format t "~{~{One ~A, two ~A.~}~%~}"
        (mapcar
         (lambda (noun) (list noun (pluralize noun)))
         '("fish" "fly" "ox" "species" "genus" "phylum" "cherub" "radius"
           "jockey" "index" "matrix" "mythos" "phenomenon" "formula")))
; => One fish, two fishes.
;    One fly, two flies.
;    One ox, two oxes.
;    One species, two specieses.
;    One genus, two genuses.
;    One phylum, two phylums.
;    One cherub, two cherubs.
;    One radius, two radiuses.
;    One jockey, two jockeys.
;    One index, two indexes.
;    One matrix, two matrices.
;    One mythos, two mythoses.
;    One phenomenon, two phenomenons.
;    One formula, two formulas.


;;; @@PLEAC@@_2.19
(defun factorize (n)
  (let ((factors (make-hash-table))
        (m n))
    (loop for i from 2
          while (<= (1+ (* i 2)) m)
          do (loop while (zerop (mod m i))
                   do (setq m (/ m i))
                      (setf (gethash i factors)
                            (1+ (gethash i factors 0)))))
    (unless (or (= 1 m) (= n m))
      (setf (gethash m factors) 1))
    (format t "~A = ~{~{~A^~A~}~^ * ~}~%"
            n (loop for factor being each hash-key of factors
                     collect (list factor (gethash factor factors))))))

; (factorize 49249597168049276)
; => 49249597168049276 = 2^2 * 29^1 * 179^1 * 103387^1 * 22941707^1


;;; @@PLEAC@@_3.0
;;; Despite  standard Common Lisp  date &  time related  functions used  in this
;;; chapter,  there are  also these  date &  time libraries  which you  might be
;;; interested  in: local-time, net-telent-date,  and date-calc.   (Packages are
;;; available      via      ASDF-INSTALL.)       There      is      also      <a
;;; href="http://cybertiggyr.com/gene/pdl/">Parsing  Dates  in Lisp</a>  webpage
;;; written by G. M. Stover containing some utility scripts.

;;; As a shortcut,  below is a small  list of available Common Lisp  date & time
;;; builtins.

;;; Constant Variable <a href="http://l1sp.org/cl/internal-time-units-per-second">INTERNAL-TIME-UNITS-PER-SECOND</a>
;;; Function <a href="http://l1sp.org/cl/decode-universal-time">DECODE-UNIVERSAL-TIME</a>
;;; Function <a href="http://l1sp.org/cl/encode-universal-time">ENCODE-UNIVERSAL-TIME</a>
;;; Function <a href="http://l1sp.org/cl/get-universal-time">GET-UNIVERSAL-TIME</a>
;;; Function <a href="http://l1sp.org/cl/get-decoded-time">GET-DECODED-TIME</a>
;;; Function <a href="http://l1sp.org/cl/sleep">SLEEP</a>
;;; Function <a href="http://l1sp.org/cl/get-internal-real-time">GET-INTERNAL-REAL-TIME</a>
;;; Function <a href="http://l1sp.org/cl/get-internal-run-time">GET-INTERNAL-RUN-TIME</a>

;;; A "decoded time" is an ordered  series of nine values (second, minute, hour,
;;; day,  month, year,  dow,  daylight-p, and  timezone)  that, taken  together,
;;; represent a point in calendar time (ignoring leap seconds).

;;; "Universal time"  is an absolute  time represented as a  single non-negative
;;; integer  --- the  number  of seconds  since  midnight, January  1, 1900  GMT
;;; (ignoring leap seconds).

(get-universal-time)
; => 3445946614

(multiple-value-list (get-decoded-time))
; => (45 23 17 13 3 2009 4 NIL -2)

;;; Current  Common   Lisp  specification   is  lacking  of   a  day-of-the-year
;;; concept.   But  we   can  easily   implement   it  using   a  decoded   time
;;; specification.     (For     hairy    details     you     can    check     <a
;;; href="http://en.wikipedia.org/wiki/Calculating_the_day_of_the_week">related
;;; wikipedia page</a> out.)

(defun day-of-the-year (&optional universal-time)
  (let* ((decoded-time (multiple-value-list
                           (decode-universal-time
                            (or universal-time
                                (get-universal-time)))))
         (day          (elt decoded-time 3))
         (month        (elt decoded-time 4))
         (year         (elt decoded-time 5)))
    (loop for m from 2 to month
          do (incf day
                   (elt
                     (multiple-value-list
                         (decode-universal-time
                          (- (encode-universal-time 0 0 0 1 m year)
                             (* 60 60 24))))
                     3)))
    day))

(format t "Today is day ~A of the current year.~%" (day-of-the-year))
; => Today is day 72 of the current year.

(day-of-the-year (encode-universal-time 59 59 23 31 12 2009))
; => 365

(format t "Today is day ~A of the current year.~%" (day-of-the-year))

;;; And here is a quick & dirty `STRFTIME' utility.

(defmacro define-time-formatters (&body formatter-specs)
  `(list
    ,@(mapcar
       (lambda (formatter-spec)
         `(cons
           ,(first formatter-spec)
           (lambda (second minute hour day month year dow daylight-p zone)
             (declare (ignorable second minute hour day month year dow daylight-p zone))
             ,@(rest formatter-spec))))
       formatter-specs)))

(defvar *time-formatters*
  (define-time-formatters
    (#\a (elt #("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun") dow))
    (#\b (elt #("Jan" "Feb" "Mar"
                "Apr" "May" "Jun"
                "Jul" "Aug" "Sep"
                "Oct" "Nov" "Dec")
              month))
    (#\d (format nil "~2,'0D" day))
    (#\H (format nil "~2,'0D" hour))
    (#j  (day-of-the-year
          (encode-universal-time
           second minute hour day month year dow zone)))
    (#\m (format nil "~2,'0D" month))
    (#\M (format nil "~2,'0D" minute))
    (#\S (format nil "~2,'0D" second))
    (#\w dow)
    (#\Y year)
    (#\z zone)))

(defun strftime (format &optional universal-time)
  (with-output-to-string (out)
    (loop for curr-index from 0
          for prev-special-p = nil then curr-special-p
          for curr-char across format
          for curr-special-p = (char-equal #\% curr-char)
          do (cond
               ((or (and prev-special-p curr-special-p)
                    (not (or prev-special-p curr-special-p)))
                (format out "~C" curr-char))
               (prev-special-p
                (format
                 out "~A"
                 (if (not (< curr-index (length format)))
                     (error "Missing directive at position ~D." curr-index)
                     (or (alexandria:when-let
                             (formatter
                              (find curr-char *time-formatters*
                                    :test #'char-equal :key #'car))
                           (apply
                            (cdr formatter)
                            (multiple-value-list
                                (decode-universal-time
                                 (or universal-time (get-universal-time))))))
                         (error "Invalid directive ~S at position ~D."
                                curr-char curr-index)))))))))

(strftime "%d %b, %Y, %a")
; => "12 Apr, 2009, Thu"

(let* ((today     (get-universal-time))
       (yesterday (- today (* 60 60 24))))
  (format t "Today    : ~A (~A)~%Yesterday: ~A (~A)~%"
          (strftime "%Y-%m-%d %H:%M:%S" today) today
          (strftime "%Y-%m-%d %H:%M:%S" yesterday) yesterday))
; => Today    : 2009-22-13 17:22:09 (3445946529)
;    Yesterday: 2009-22-12 17:22:09 (3445860129)


;;; @@PLEAC@@_3.1
;;;-----------------------------
;; use GET-DECODED-TIME to fetch the time
(multiple-value-bind
      (second minute hour date month year day-of-week dst-p tz)
    (get-decoded-time)
  year) ; prints out year using standard library

;; alternatively date-calc provides overlapping functionality
(multiple-value-bind (year month day h m s)
    (today-and-now)                     ; imported from date-calc
  year)                                 ; date-calc approach 

;; how to print out current date as "YYYY-MM-DD" (in approved ISO 8601 fashion)
(multiple-value-bind (year month day) 
    (today)                             ;imported from date-calc
  (format t "The current date is ~A-~2,'0d-~2,'0d" year month day))

;; Alternatively, you could use the format-time function from the
;; CyberTiggyr-Time package:
(format-time t "%Y-%m-%d" (get-universal-time))

;; As you can see, format-time operates on epoch time

;;; @@PLEAC@@_3.2 
;;;-----------------------------
;; to encode time into universal time using date-calc
(multiple-value-bind
      (second minute hour date month year day-of-week dst-p tz)
    (get-decoded-time)
  (encode-universal-time second minute hour date month year))

;; The last two return values for get-decoded-time correspond to
;; daylight savings and the timezone.  Both are useful for
;; timezone-related arithmetic.

;;; @@INCOMPLETE@@
;; An example of a GMT computation with and without daylight savings
;; is appropriate here since the built-in perl functions handle this.

;;; @@PLEAC@@_3.3
;;;-----------------------------
(let ((time (get-universal-time))) ; get epoch seconds
  (multiple-value-bind
      (second minute hour day month year day-of-week dst-p tz)
      (decode-universal-time time) ; decode and...
    (list day month year hour minute second))) ; return

;;; @@PLEAC@@_3.4
;;;-----------------------------

;; when using universal time you add or subtract seconds
;; here we add one hour
(let ((firstdate 
       (encode-universal-time 0 12 6 23 11 2006))
      (onehour (* 60 60 1)))
  (+ onehour firstdate))

;; or you could use date-calc function
;; here we'll add one day
(add-delta-ymdhms 2006 11 24 18 12 0  0 0 1 0 0 0)

;;; @@PLEAC@@_3.5
;;;-----------------------------
;; We'll use the epoch seconds to perform subtraction, 
;; then divide by seconds per day
(let ((first (encode-universal-time 52 45 20 13 12 1901))
      (second (encode-universal-time 7 14 3 19 1 2038)))
  (float (/ (- second first) (* 60 60 24))))

;; method two uses delta-days from the date-calc package:
(delta-days 1901 12 13 2038 1 19)

;; delta-days does not yet have the granularity of seconds, minutes or hours. 

;;; @@PLEAC@@_3.6
;;;-----------------------------
;; The week of the year is computed as follows:
(week-number 2006 12 1)      ; week-of-year is imported from date-calc

;; similar functions exist for day of week, day of year, etc.

;;; @@PLEAC@@_3.7
;;;-----------------------------
(parse-time "2006-08-20")

;; PARSE-TIME can recognize many of the commonly found date formats

; format-time comes with several ways to format...
(format-time t *format-time-date* (get-universal-time))

; results in: 25 Nov 2006

(format-time t *format-time-iso8601-short* (get-universal-time))

; results in: 20061125T172917 -5

(format-time t "%Y-%m-%d" (get-universal-time))

; results in: 2006-11-25

;;; @@PLEAC@@_4.0
;;;-----------------------------
(setf nested '("this" "that" "the" "other")
(setf nested '("this" "that" ("the" "other")))
;;;-----------------------------
(setf tune '("The" "Star-Spangled" "Banner"))

;;; @@PLEAC@@_4.1
;;;-----------------------------
(setf a '("quick" "brown" "fox")
;;;-----------------------------
(setf a '("Why" "are" "you" "teasing" "me?"))
;;;-----------------------------
(setf lines (regex-replace-all (create-scanner "^\\s*(.+)" :multi-line-mode t )
"    The boy stood on the burning deck,
    It was as hot as glass.
" "\\1"))
;;;----------------------------- 

;;; You don't really need an explicit call to the CL equivalent of
;;; Perl's die().  Its behavior is the same by default (it does put
;;; you into the CL debugger, but that's not a bad thing).  You could,
;;; alternatively, handle this error with HANDLER-BIND or HANLDER-CASE
;;; if you wanted to be more precisely like the Perl version.
(let ((bigarray '()))
  (with-open-file (data "mydatafile")
    (loop for line = (read-line data nil nil)
       while line
       do (push (string-right-trim #(#\Newline #\Return)
                                   line) bigarray))))
;;;-----------------------------
(setf banner "The Mines of Moria")
;;;-----------------------------
(setf name "Gandalf")
(setf banner (format nil "Speak ~A and enter!" name))
(setf banner "Speak $name and welcome!")
;;;-----------------------------
(setf his-host "www.perl.com")
#+sbcl
(setf host-info (with-output-to-string (output)
                  (sb-ext:run-program "nslookup" `(,his-host) :search t :output output)))
;; There's no equivalent to Perl's qx
;;;-----------------------------
(setf banner '("Costs" "only" "$4.95"))
(setf banner (split " " "Costs only $4.95"))
;;;-----------------------------
(setf brax '(#\( #\) #\< #\> #\{ #\} #\[ #\]))
(setf rings '("Nenya" "Narya" "Vilya"))
(setf tags '("LI" "TABLE" "TR" "TD" "A" "IMG" "H1" "P"))
(setf sample '("The" "vertical" "bar" "(|)" "looks" "and" "behaves" "like" "a" "pipe."))
;;;-----------------------------
;; No equivalent in CL (would just be the same as above)
;;;-----------------------------
;; No equivalent in CL (would just be the same as above)
;;;-----------------------------

;;; @@PLEAC@@_4.2
;;;-----------------------------
(defun commify-series (list)
  (case (length list)
    (0 "")
    (1 (car list))
    (2 (format nil "~{~A~^ and ~}" list))
    (t (concatenate 'string
                    (format nil "~{~A~^, ~}" (butlast list))
                    (format nil " and ~A" (car (last list)))))))
;;;-----------------------------
(let ((array '("red" "yellow" "green")))
  (format t "I have ~{~A~} marbles.~%" array)
  (format t "I have ~{~A~^ ~} marbles.~%" array))
;;I have redyellowgreen marbles.
;;I have red yellow green marbles.
;;;-----------------------------
;; download the following standalone program
;;;; commify_series - show proper comma insertion in list output

(defparameter *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")))

(defun commify_series (list)
  (let ((sepchar (if (find-if #'(lambda (string) 
                                  (find #\, string))
                              list)
                     "; " ", ")))
    (case (length list)
      (0 "")
      (1 (car list))
      (2 (format nil "~{~a~^ and ~}" list))
      (t (concatenate 'string
                      (format nil
                              "~{~}" 
                              (concatenate 'string "~a~^" sepchar)
                              (butlast list))
                      (format nil " and ~a" (car (last list))))))))

(mapc #'(lambda (list)
          (format t "The list is: ~a.~%" (commify_series list)))
      *lists*)


;;;-----------------------------

;;; @@PLEAC@@_4.3
;;;-----------------------------
;; grow or shrink MY-ARRAY (assuming it was created with :ADJUSTABLE
;; set to T)
(adjust-array my-array (1+ new-last-element-index-number))
;;;-----------------------------
;; There's no auto-creation of array elements.
;;;-----------------------------
(defparameter *people* (make-array 4
                                   :initial-contents '("Crosby" "Stills" "Nash" "Young")
                                   :adjustable t))
(defun what-about-that-array ()
  (format t
          "The array now has ~D elements
The index of the last element is ~D
Element #3 is ~A~%"
          (length *people*)
          (1- (length *people*))
          (aref *people* 3)))
(what-about-that-array)
;;;-----------------------------
;;The array now has 4 elements
;;The index of the last element is 3
;;Element #3 is Young
;;;-----------------------------
(adjust-array *people* (1- (length *people*)))
(what-about-that-array)
;;;-----------------------------
;; Evaluating WHAT-ABOUT-THAT-ARRAY now results in an error because
;; there is no 3rd element, and, unlike Perl, CL doesn't just return
;; the empty string in that case.
;;;-----------------------------
(adjust-array *people* 10001)
(what-about-that-array)
;;;-----------------------------
;;The array now has 10001 elements
;;The index of the last element is 9999
;;Element #3 is 0
;;;-----------------------------
(setf (aref *people* 10000) nil)
;;;-----------------------------

;;; @@PLEAC@@_4.4
;;;-----------------------------
(dolist (item list)
  ;; do something with ITEM
  )
;;;-----------------------------
(dolist (user bad-users)
  (complain user))
;;;-----------------------------
(dolist (var (sort (loop for x being the hash-keys of axl
                      collect x) #'<))
  (format t "~A=~A~%" var (gethash var ENV)))
;;;-----------------------------
(dolist (user all-users)
  (let ((disk-space (get-usage user)))
    (when (> disk-space +max-quota+)
      (complain user))))
;;;-----------------------------
#+sbcl
(dolist (line (split "\\n"
                     (with-output-to-string (output)
                       (sb-ext:run-program "who" nil :search t :output output))))
  (when (scan "tchrist" line)
    (format t "~A~%" line)))
;;;-----------------------------
(loop for line = (read-line fh nil :eof nil) ; LINE is set to the line just read
   until (eq line :eof)
   do 
   (dolist (chunk (split "\\s+"         ; LINE is split on whitespace
                                        ; then CHUNK is set to each chunk in turn
                         (chomp line))) ; LINE has a trailing \n removed, if it had one
     (format t "~A"                     ; CHUNK is printed
             (reverse chunk))))         ; the characters in CHUNK are reversed
;;;-----------------------------
(map nil #'(lambda (item)
             (format t "i = ~A~%" item))
     my-array)
;;;-----------------------------
(setf my-array #(1 2 3))
(map-into my-array #'(lambda (item) (decf item)) my-array)
my-array
;; #(0 1 2)

;; multiply everything in a and b by seven
(setf a #(.5 3) b #(0 1))
(map-into a #'(lambda (item) (* item 7)) a)
(map-into b #'(lambda (item) (* item 7)) b)
(format t "~{~A~^ ~} ~{~A~^ ~}~%" (coerce a 'list) (coerce b 'list))
;; 3.5 21 0 7
;;;-----------------------------
;; The following macro is mostly like Perl's foreach, in the sense
;; that you can pass in as many references to sequences or "scalars"
;; as you want and it will iterate over them and allow you to modify
;; them.  Unlike the Perl code, it sets the variable IT to each
;; element rather than $_.  Also, you have to just pass in the hash
;; table directly, not a flattened list of hash keys.
(defmacro perl-foreach ((&rest refs) &body body)
  (let* ((gensyms (loop repeat (length refs) collect (gensym))))
    (list*
     'let
     (mapcar #'list gensyms refs)
     (loop
        for ref in refs
        and indirect-ref in gensyms
        collect
        `(typecase ,indirect-ref
           (hash-table 
            (maphash #'(lambda (key value)
                         (declare (ignore value))
                         (symbol-macrolet ((it (gethash key ,indirect-ref)))
                           ,@body))
                     ,indirect-ref))
           ((and (or vector list) (not string))
            (map-into ,indirect-ref
                      #'(lambda (it)
                          ,@body
                          it)
                      ,indirect-ref))
           (t 
            (symbol-macrolet ((it ,ref))
              ,@body)))))))

;; trim whitespace in the scalar, the list, the array, and all the
;; values in the hash
(perl-foreach (scalar my-list my-array my-hash)
  (setf it (regex-replace "^\\s+" it ""))
  (setf it (regex-replace "\\s+$" it "")))
;;;-----------------------------
;; The Perl code in this subsection is Perl-specific (demonstrating
;; the shorthand syntax for "foreach").
;;;-----------------------------

;;; @@PLEAC@@_4.5
;;;-----------------------------
;; iterate over elements of array in ARRAYREF (but if you intend to
;; modify the elemnts, it will only modify non-"scalar" elements such
;; as lists, structures, etc).
(map 'array #'(lambda (item)
                ;; do something with ITEM
                )
     arrayref)

;; or you can use LOOP
(loop for item across arrayref
     do ;; do something with ITEM
     )

;; to modify the array contents (even if they're "scalars" like
;; numbers)
(map-into arrayref #'(lambda (item)
                       ;; do something with ITEM
                       )
          arrayref)

;; or you can use ITER
(iter (for i index-of-vector arrayref)
      ;; do something with (aref arrayref i)
      )

;; As a side note, for lists you could also do the following (won't
;; allow modifying "scalar" elements of the list).
(dolist (item list)
  ;; do something with ITEM
  )
;;;-----------------------------
(defparameter *fruits* #("Apple" "Blackberry"))
(setf fruit-ref *fruits*)
(loop for fruit across fruit-ref
   do (format t "~A tastes good in a pie.~%" fruit))
;;Apple tastes good in a pie.
;;Blackberry tastes good in a pie.
;;;-----------------------------
(loop for i below (length fruit-ref)
   do (format t "~A tastes good in a pie.~%" (svref fruit-ref i)))
;;;-----------------------------
(setf (gethash :felines *namelist*) *rogue-cats*)
(dolist (cat (gethash :felines *namelist*))
  (format t "~A purrs hypnotically..~%" cat))
(format t "--More--~%You are controlled.~%")
;;;-----------------------------
(loop for i below (length (gethash :felines *namelist*))
   do (format t "~A purrs hypnotically..~%" (elt (gethash :felines *namelist*) i)))
;;;-----------------------------

;;; @@PLEAC@@_4.6
;;;-----------------------------
(defparameter *seen* (make-hash-table :test 'equal))
(defparameter *uniq* '())

(dolist (item my-list)
  (unless (gethash item *seen*)
    ;; if we are here, we have not seen it before
    (setf (gethash item *seen*) 1)
    (push item *uniq*)))
;;;-----------------------------
(clrhash *seen*)
(dolist (item my-list)
  (when (= (incf (gethash item *seen* 0)) 1)
    (push item *uniq*)))
;;;-----------------------------
(clrhash *seen*)
(dolist (item my-list)
  (when (= (incf (gethash item *seen* 0)) 1)
    (some-func item)))
;;;-----------------------------
(clrhash *seen*)
(dolist (item my-list)
  (incf (gethash item *seen* 0)))
(setf *uniq* (loop for k being the hash-keys of *seen* collect k))
;;;-----------------------------
(clrhash *seen*)
(setf *uniq* (perl-grep my-list (= 1 (incf (gethash it *seen* 0)))))
;;;-----------------------------
;; generate a list of users logged in, removing duplicates
(defparameter *ucnt* (make-hash-table :test 'equal))

(defmacro dostream ((var stream) &body body)
  "Like DOLIST except iterates over the lines of STREAM.  Does not
close STREAM."
  (let ((s (gensym "stream-"))
        (eof (gensym "eof-")))
    `(let ((,s ,stream))
       (do ((,var (read-line ,s nil ',eof nil) 
                  (read-line ,s nil ',eof nil)))
           ((eql ,var ',eof))
         ,@body))))

#+sbcl
(with-open-stream (s (process-output 
                      (sb-ext:run-program "who" nil :search t :output :stream :wait nil)))
  (dostream (who s)
    ;; kill from first space till end-of-line, yielding username
    (setf who (regex-replace "\\s.*$" who "")) 
    (incf (gethash who *ucnt* 0)))) ; record the presence of this user
;; extract and print unique keys
(defparameter *users* (sort (loop for k being the hash-keys of *ucnt* collect k) #'string=))
(format t "users logged in: ~{~A~^ ~}~%" *users*)
;;;-----------------------------

;;; @@PLEAC@@_4.7
;;;-----------------------------
;; assume A and B are already loaded
(defparameter *seen* (make-hash-table :test 'equal)) ; lookup table to test membership of B
(defparameter *a-only* '())                          ; answer

;; build lookup table
(loop for item in b do (setf (gethash item *seen*) 1))

;; find only elements in A and not in B
(dolist (item a)
  (unless (gethash item *seen*)
    ;; it's not in SEEN, so add to *A-ONLY*
    (push item *a-only*)))
;;;-----------------------------
;; The Perl example here isn't substantially different from the above
;; in CL.
;;;-----------------------------
(dolist (item a)
  (unless (gethash item *seen*)
    (push item *a-only*))
  (setf (gethash item *seen*) 1)) ; mark as seen
;;;-----------------------------
(setf (gethash "key1" my-hash) 1)
(setf (gethash "key2" my-hash) 2)
;;;-----------------------------
(loop
   for key in '("key1" "key2")
   and value in '(1 2)
   do (setf (gethash key my-hash) value))
;;;-----------------------------
(loop
   for key in b
   do (setf (gethash key my-hash) nil))
;;;-----------------------------
(loop
   for key in b
   do (setf (gethash key my-hash) (loop repeat (length b) collect 1)))
;;;-----------------------------

;;; @@PLEAC@@_4.8
;;;-----------------------------
(defparameter *a* '(1 3 5 6 7 8))
(defparameter *b* '(2 3 5 7 9))

(defvar *union*)
(defvar *isect*)
(defvar *diff*)
(defparameter *union-hash* (make-hash-table))
(defparameter *isect-hash* (make-hash-table))
(defparameter *count* (make-hash-table))
;;;-----------------------------
;; don't actually do this, use instead the built-ins shown at the end
(dolist (e *a*)
  (setf (gethash e *union-hash*) 1))

(dolist (e *b*)
  (when (gethash e *union-hash*)
    (setf (gethash e *isect-hash*) 1))
  (setf (gethash e *union-hash*) 1))

(setf *union* (loop for k being the hash-keys of *union-hash* collect k))
(setf *isect* (loop for k being the hash-keys of *isect-hash* collect k))

;; or you could use the built ins
(setf *union* (union *a* *b*))
(setf *isect* (intersection *a* *b*))
;;;-----------------------------
(perl-foreach (*a* *b*)
  (and (prog1 (gethash it *union-hash*)
         (incf (gethash it *union-hash* 0)))
       (incf (gethash it *isect-hash* 0))))

(setf *union* (hash-keys *union-hash*)) ; HASH-KEYS defined in Appendix
(setf *isect* (hash-keys *isect-hash*))
;;;-----------------------------
(perl-foreach (*a* *b*) (incf (gethash it *count* 0)))
  
(loop
   for e being the hash-keys of *count* using (hash-value count)
   do
   (push e *union*)
   (case count
     (2 (push e *isect*))
     (t (push e *diff*))))
;;;-----------------------------
;; Without writing a special macro, there'd be no obvious difference
;; from the previous example.
;;;-----------------------------

;;; @@PLEAC@@_4.9
;;;-----------------------------
;; push
(setf array1 (append array1 array2))
;;;-----------------------------
(setf array1 `(,@array1 ,@array2))
;;-----------------------------
(let ((members '("Time" "Flies"))
      (initiates '("An" "Arrow")))
  (setf members (append members initiates))
;; members is now ("Time" "Flies" "An" "Arrow")
;;;-----------------------------
  (setf members `(,@(subseq members 0 2) "Like" ,@initiates))
  (format t "~{~a~^ ~}~%" members)
  (setf members `("Fruit" ,@(subseq members 1)))
  (setf members `(,@(subseq members 0 (- (length members) 2)) "A" "Banana"))
  (format t "~{~a~^ ~}~%" members))
;;;-----------------------------
;;Time Flies Like An Arrow
;;Fruit Flies Like A Banana
;;;-----------------------------

;;; @@PLEAC@@_4.10
;;;-----------------------------
;; reverse ARRAY into REVERSED
(setf reversed (reverse *array*))
;;-----------------------------
(do ((i (1- (array-dimension *array* 0)) (1- i)))
    ((minusp i))
  ;; do something with (aref array i)
  )
;;;-----------------------------
;; two-step: sort then reverse
;; SORT is destructive, hence STABLE-SORT
(setf ascending (stable-sort users 'string-lessp))
(setf descending (reverse ascending))

;; one-step: sort with reverse comparison
(setf descending (reverse (stable-sort users 'string-lessp)))
;;;-----------------------------

;;; @@PLEAC@@_4.11
;;;-----------------------------
;; Removing N elements from front of MY-ARRAY requires 2 steps in CL.
(setf front (subseq my-array 0 n))
(setf my-array (subseq my-array n))

;; We can write a macro to mimic Perl's behavior, however.
(defmacro perl-splice (sequence-place &optional (offset 0) length replacement-sequence)
  (let* ((seq (gensym "SEQUENCE-PLACE-"))
         (off-arg (gensym "OFFSET-ARG-"))
         (off (gensym "OFFSET-"))
         (len (gensym "LENGTH-"))
         (end (gensym "END-"))
         (rep (gensym "REPLACEMENT-SEQUENCE-"))
         (left-part (list `(subseq ,seq 0 ,off)))
         (right-part (when length
                       (list `(subseq ,seq ,end)))))
    `(let* ((,seq ,sequence-place)
            (,off-arg ,offset)
            (,off (if (minusp ,off-arg)
                      (+ (length ,seq) ,off-arg)
                      ,off-arg))
            (,len ,length)
            (,end (when ,len
                    (if (minusp ,len)
                        (+ (length ,seq) ,len)
                        (+ ,off ,len))))
            (,rep ,replacement-sequence))
       (prog1 (subseq ,seq ,off ,end)
         (when (or ,rep (not (eql ,off ,end)))
           (setf ,sequence-place (concatenate (typecase ,seq
                                                (cons 'list)
                                              (t 'vector))
                                            ,@left-part
                                            ,rep
                                            ,@right-part)))))))

;; Now the syntax is almost exactly the same.
(setf front (perl-splice my-array 0 n))
(setf end (perl-splice my-array 0 (- n)))
;;;-----------------------------
(defmacro shift2 (sequence)
  `(perl-splice ,sequence 0 2))

(defmacro pop2 (sequence)
  `(perl-splice ,sequence -2))
;;;-----------------------------
(defparameter *friends* '(Peter Paul Mary Jim Tim))

(destructuring-bind (this that) (shift2 *friends*)
  ;; THIS contains PETER, THAT has PAUL, and
  ;; *FRIENDS* has MARY, JIM, and TIM
  )
  
(defparameter *beverages* #(Dew Jolt Cola Sprite Fresca))
(let ((pair (pop2 *beverages*)))
  ;; (aref pair 0) contains Sprite, (aref pair 1) has Fresca,
  ;; and *beverages* has #(DEW JOLT COLA)
  )
;;;-----------------------------
(setf (aref line 5) my-list)
(setf got (pop2 (aref line 5)))
;;;-----------------------------

;;; @@PLEAC@@_4.12
;;;-----------------------------
(let ((match (find item *sequence*)))
  (cond
    (match
     ;; do something with MATCH
     )
    (t
     ;; unfound
     )))
;;;-----------------------------
(let ((match-idx (position item *sequence*)))
  (cond
    (match-idx
     ;; found in (elt *sequence* match-idx) (any sequence)
     ;; or (svref *sequence* match-idx) if you know it's a vector 
     )
    (t
     ;; unfound
     )))
;;;-----------------------------
(defstruct employee name category) ; just to make example work
(format t "Highest paid engineer is: ~A~%"
        (employee-name (find 'engineer *employees* :key 'employee-category :from-end t)))
;;;-----------------------------
;; Don't do this, just intended how one could match the Perl example.
(let ((i
       (loop for idx below (length *array*)
          ;;
          do (when criterion ;; put criterion here
               (return idx)))))
  (if (< i (length *array*))
      (progn
        ;; found and I is the index
        )
      (progn
        ;; not found
        )))
;;;-----------------------------
        
;;; @@PLEAC@@_4.13
;;;-----------------------------
(setf matching (find-if-not #'test list))
;;-----------------------------
(let ((matching '()))
  (dolist (item list)
    (when (test item) (push item matching))))
;;;-----------------------------
(setf bigs (remove-if-not #'(lambda (num) (> num 1000000)) nums))
(setf pigs (loop for user being the hash-keys of users using (hash-value uid)
              when (> uid 1e7)
              collect user))

;;;-----------------------------
#+sbcl
(remove-if-not #'(lambda (line)
                   (scan "^gnat " line))
               (split #\Newline
                               (with-output-to-string (output)
                                 (sb-ext:run-program "who" nil :search t :output output)q)))
;;;-----------------------------
;;; Assumes DEFSTRUCT or DEFCLASS of EMPLOYEE with a POSITION slot.
(setf engineers (remove "Engineer" employees :key #'employee-position :test-not 'string=))
;;-----------------------------
(setf secondary-assistance (remove-if-not #'(lambda (applicant)
                                              (and (>= (applicant-income applicant) 26000)
                                                   (<  (applicant-income applicant) 30000)))
                                          applicants))
;;;-----------------------------

;;; @@PLEAC@@_4.14
;;;-----------------------------
(setf sorted (stable-sort unsorted '<))
;;;-----------------------------
;; PIDS is an unsorted list of process IDs
(dolist (pid (stable-sort pids '<))
  (format t "~A~%" pid))
(format t "Select a process ID to kill:~%")
(let ((pid (read)))
  (etypecase pid
    (integer (sb-posix:kill pid sb-posix:sigterm)
             (sleep 2)
             (ignore-errors
               (sb-posix:kill pid sb-posix:sigkill)))))
;;;-----------------------------
(setf descending (stable-sort unsorted '>))
(defpackage :sort-subs (:use cl))
(in-package :sort-subs)
(defun revnum (a b)
  (< b a))

(defpackage :other-pack (:use cl))
(in-package :other-pack)
(defparameter *all* (stable-sort #(4 19 8 3) 'sort-subs::revnum))
;;;-----------------------------
(setf *all* (stable-sort #(4 19 8 3) '>))
(in-package :cl-user) 
;;;-----------------------------


;;; @@PLEAC@@_4.15
;;; There is nothing special about sorting  a list by computable field in Common
;;; Lisp. One just need to pass an appropriate `KEY' function to `SORT'.

(defvar *sample-list*
  '((1 -1 0.1 "one")
    (2 -2 0.2 "two")
    (3 -3 0.3 "three")))

;;; Just  keep in  mind that,  `SORT' works  destructively. (That's  why  we use
;;; `COPY-LIST' in the below expression.)
(sort (copy-list *sample-list*) #'< :key #'first)
; => ((1 -1 0.1 "one")
;     (2 -2 0.2 "two")
;     (3 -3 0.3 "three"))

(sort (copy-list *sample-list*) #'string< :key #'fourth)
; => ((1 -1 0.1 "one")
;     (3 -3 0.3 "three")
;     (2 -2 0.2 "two"))

(sort (copy-list *sample-list*) #'<
      :key (lambda (list) (* (second list) (third list))))
; => ((3 -3 0.3 "three")
;     (2 -2 0.2 "two")
;     (1 -1 0.1 "one"))


;;; @@PLEAC@@_4.16
;;;-----------------------------
;;; The following aren't efficient on long lists
(setf circular `(,@(last circular) ,@(nbutlast circular))) ; the last shall be first
(setf circular `(,@(cdr circular) ,(car circular))) ; and vice versa
;;;-----------------------------
;;; There is probably a less ugly way to do this
(defmacro grab-and-rotate (list)
  `(prog1 (car ,list)
     (setf ,list `(,@(cdr ,list) ,(car ,list)))))

(let ((processes '(1 2 3 4 5)))
  (loop 
     (let ((process (grab-and-rotate processes)))
       (format t "Handling process ~A~%" process)
       (sleep 1))))    
;;;-----------------------------

;;; @@PLEAC@@_4.17
;;;-----------------------------
(defun fisher-yates-shuffle (vector)
  "Randomly shuffle elements of VECTOR."
  (loop for i from (1- (length vector)) downto 1
     for j = (random i)
     unless (= i j)
     do (rotatef (aref vector i) (aref vector j)))
  vector)

(fisher-yates-shuffle vector)           ; permutes VECTOR in place
;;;-----------------------------
(defun shuffle (vector)
  "Return a fresh permuted copy of VECTOR."
  (let* ((n-permutations (factorial (length vector)))
         (permutation (nth-permutation (random n-permutations)
                                       (length vector))))
    (map 'vector (lambda (i) (aref vector i)) permutation)))
;;;
(defun naive-shuffle (vector)
  (loop with n = (length vector)
        for i from 0 below n
        for j = (random n)
        do (rotatef (aref vector i) (aref vector j)))
  vector)


;;; @@PLEAC@@_4.18
(defun print-matrix (matrix column-len)
  (format t (format nil "~~{~~{~~@[~~~DA~~^ ~~]~~}~~%~~}" column-len) matrix))

(defun pop-matrix-column (matrix)
  (when matrix
    (let ((elt (caar matrix)))
      (setf (car matrix) (cdar matrix))
      (cons elt (pop-matrix-column (cdr matrix))))))

(defun transpose-matrix (matrix)
  (when (car matrix)
    (cons (pop-matrix-column matrix)
          (transpose-matrix matrix))))

(defun parse-matrix-row (string &key count)
  (when string (ppcre:split "\\s+" string :limit count)))

(defun parse-matrix (stream)
  (let ((first-row (parse-matrix-row (read-line stream nil nil))))
    (when first-row
      (let* ((n-columns (length first-row))
             (matrix
              (cons first-row
                    (loop for row = (parse-matrix-row
                                     (read-line stream nil nil)
                                     :count n-columns)
                          while row collect row)))
             (column-len
              (reduce #'max matrix
                      :key (lambda (vals)
                             (reduce #'max vals :key #'length)))))
        (print-matrix (transpose-matrix matrix) column-len)))))

(with-input-from-string
    (in "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")
  (parse-matrix in))
; => 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       


;;; @@PLEAC@@_4.19
;;; A naive `FACTORIAL' implementation.
(defun factorial (n)
  (if (< n 2) 1 (* n (factorial (1- n)))))

;;; A tail-recursive derivative of the above `FACTORIAL' implementation.
(defun tco-factorial (n)
  (labels ((iter (m acc)
             (if (< n m) acc (iter (1+ m) (* m acc)))))
    (iter 1 1)))

(tco-factorial (expt 2 8))
; => 857817775342842654119082271681232625157781520279485619859655650377269452553
;    147589377440291360451408450375885342336584306157196834693696475322289288497
;    426025679637332563368786442675207626794560187968867971521143307702077526646
;    451464709187326100832876325702818980773671781454170250523018608495319068138
;    257481070252817559459476987034665712738139286205234756808218860701203611083
;    152093501947437109101726968262861606263662435022840944191408424615936000000
;    000000000000000000000000000000000000000000000000000000000

(defun permutations (items &key test &aux (test (or test #'eql)))
  (if (null items) '(())
      (mapcan
       (lambda (item)
         (mapcar (lambda (permutation) (cons item permutation))
                 (permutations (remove item items :test test) :test test)))
       items)))

(permutations '("man" "bites" "dog") :test #'string-equal)
; => (("man" "bites" "dog")
;     ("man" "dog" "bites")
;     ("bites" "man" "dog")
;     ("bites" "dog" "man")
;     ("dog" "man" "bites")
;     ("dog" "bites" "man"))


;;; @@PLEAC@@_5.0
;;;-----------------------------
(setf age (make-hash-table :test 'equal))

(setf (gethash "Nat" age) 24
      (gethash "Jules" age) 25
      (gethash "Josh" age) 17)
;;-----------------------------
(mapcar #'(lambda (l)
            (setf (gethash (car l) age) (cdr l)))
        '(("Nat" . 24)
          ("Jules" . 25)
          ("Josh" . 17)))
;;-----------------------------
(defparameter *food-color* (make-hash-table :test 'equal))

(mapcar #'(lambda (l)
            (setf (gethash (car l) *food-color*) (cdr l)))
        '(("Apple" . "red")
          ("Banana" . "yellow")
          ("Lemon" . "yellow")
          ("Carrot" . "orange")))
;;;-----------------------------
(mapcar #'(lambda (l)
            (setf (gethash (car l) *food-color*) (cdr l)))
        '((Apple . "red")
          (Banana . "yellow")
          (Lemon . "yellow")
          (Carrot . "orange")))
;;;-----------------------------

; @@PLEAC@@_5.1
;;;-----------------------------
(setf (gethash key hash) value)
;;;-----------------------------
;; *FOOD-COLOR* defined per the introduction
(setf (gethash "Raspberry" *food-color*) "pink")

(format t "Known foods:~%~{~A~%~}"
        (loop for f being the hash-keys of *food-color*
           collect f))
;;Known foods:
;;Apple
;;Banana
;;Lemon
;;Carrot
;;;-----------------------------

;;; @@PLEAC@@_5.2
;;;-----------------------------
;; does HASH have a value for KEY ?
(if (nth-value 1 (gethash key hash))
    (progn
      ;; it exists
      )
    (progn
      ;; it doesn't
      ))
;;;-----------------------------
;; *FOOD-COLOR* per the introduction
(dolist (name '("Banana" "Martini"))
  (format t "~A is a ~A.~%"
          name
          (if (nth-value 1 (gethash name *food-color*))
              "food" "drink")))

;;Banana is a food.
;;Martini is a drink.
;;;-----------------------------
(setf age (make-hash-table :test 'equal))
(setf (gethash "Toddler" age) 3)
(setf (gethash "Unborn" age) 0)
(setf (gethash "Phantasm" age) nil)
(dolist (thing '("Toddler" "Unborn" "Phantasm" "Relic"))
  (format t "~a: " thing)
  (multiple-value-bind (defined exists)
      (gethash thing age)
      (when exists
        (format t "Exists ")
        (when defined 
          (format t "Defined ")
          ;; 0 is "true" in CL, so explicitly mimic Perl
          (unless (zerop defined)
            (format t "True ")))))
  (format t "~%"))

;;Toddler: Exists Defined True 
;;Unborn: Exists Defined 
;;Phantasm: Exists 
;;Relic: 
;;;-----------------------------
;;; @@INCOMPLETE@@

;;; @@PLEAC@@_5.3
;;;-----------------------------
;; remove KEY and its value from HASH
(remhash key hash)
;;;-----------------------------
;; *FOOD-COLOR* as per Introduction
(defun print-foods ()
  (let ((foods (hash-keys *food-color*))) ; HASH-KEYS defined in Appendix
    (format t "Keys: ~{~A~^ ~}~%Values: ~{~A~^ ~}~%" 
            foods
            (loop for food in foods
               collect (or (gethash food *food-color*)
                           "(undef)")))))

(format t "Initially~%")
(print-foods)

(format t "~%With Banana undef~%")
(setf (gethash "Banana" *food-color*) 

(format t "~%With Banana deleted~%")
(remhash "Banana" *food-color*)
(print-foods)

;; Initially
;; Keys: Apple Banana Lemon Carrot
;; Values: red yellow yellow orange
;;
;; With Banana undef
;; Keys: Apple Banana Lemon Carrot
;; Values: red (undef) yellow orange
;; 
;; With Banana deleted
;; Keys: Apple Lemon Carrot
;; Values: red yellow orange
;;;-----------------------------
(mapc #'(lambda (key) (remhash key *food-color*)) '("Banana" "Apple" "Cabbage"))
;;;-----------------------------
    
;;; @@PLEAC@@_5.4
;;;-----------------------------
(loop for key being the hash-keys of hash using (hash-value value)
     ;; do something with KEY and VALUE
     )
;;;-----------------------------
(maphash #'(lambda (key value)
             ;; do something with KEY and VALUE
             )
         hash)
;;;-----------------------------
;; *FOOD-COLOR* per the introduction
(loop for food being the hash-keys of *food-color* using (hash-value color)
     do (format t "~A is ~A.~%" food color))
;; Apple is red.
;; Banana is yellow.
;; Lemon is yellow.
;; Carrot is orange.
;;;-----------------------------
(maphash #'(lambda (food color)
             (format t "~A is ~A.~%" food color))
         *food-color*)
;; Apple is red.
;; Banana is yellow.
;; Lemon is yellow.
;; Carrot is orange.
;;;-----------------------------
;; No equivalent
;;;-----------------------------
(loop for food in (sort (hash-keys *food-color*) 'string-lessp)
   do (format t "~A is ~A~%" food (gethash food *food-color*)))
;; Apple is red
;; Banana is yellow
;; Carrot is orange
;; Lemon is yellow
;;;-----------------------------
;; Not sure what the following Perl is supposed to do:
;;while ( ($k,$v) = each %food_color ) {
;;    print "Processing $k\n";
;;    keys %food_color;               # goes back to the start of %food_color
;;}
;;;-----------------------------
(use-package :cl-ppcre)
(use-package :iterate)

;; The following handles the case that the Perl handles where there is
;; no filename (and it then opens '-'.  To do the same thing you'd do
;; something like: (countfrom *standard-input*) and this method would
;; automatically get triggered instead of the one requiring a
;; filename.
(defmethod countfrom ((stream stream))
  (let ((from (make-hash-table :test 'equal)))
    (with-open-stream (input stream)
      (iter (for line in-stream input using 'read-line)
            (register-groups-bind (person) ("^From: (.*)\\s" line)
              (incf (gethash person from 0)))))
    (loop for person in (sort (hash-keys from) 'string-lessp)
       do (format t "~A: ~A~%" person (gethash person from)))))

;; This method is a bit of a hack in that it shouldn't really assume
;; that the string designates a filename, but for the purposes of this
;; example that seems ok.  Note that it just calls OPEN directly
;; without WITH-OPEN-FILE because it knows that the STREAM version of
;; this method will always close it.
(defmethod countfrom ((filename string))
  (countfrom (open filename)))
;;;-----------------------------


;;; @@PLEAC@@_5.5
(defvar *table* (make-hash-table :test #'equal))

(dotimes (i 10)
  (setf (gethash (format nil "~R" i) *table*) i))

(maphash (lambda (key val) (format t "~A~%" (cons key val))) *table*)
; => ("nine"  . 9)
;    ("eight" . 8)
;    ("seven" . 7)
;    ("six"   . 6)
;    ("five"  . 5)
;    ("four"  . 4)
;    ("three" . 3)
;    ("two"   . 2)
;    ("one"   . 1)
;    ("zero"  . 0)

;;; Unfortunately there doesn't exist a  `FORMAT' directive for hash tables. But
;;; one can easily override existing `PRINT-OBJECT' method for hash tables.

;;; @@PLEAC@@_5.6
;;; There doesn't exist a portable way of preserving insertion order of contents
;;; of a hash table in Common Lisp.   (Neither there is a rational behind such a
;;; feature, IMHO.)  On the  other hand, one  can keep  a history of  hash table
;;; modifications separately.

(defun make-ordered-hash-table (&rest make-hash-table-args)
  (cons (apply #'make-hash-table make-hash-table-args) 0))

(defun setkey (table key val)
  (setf (gethash key (car table)) (cons (incf (cdr table)) val)))

(defun remkey (table key)
  (remhash key (car table)))

(defun getkey (table key &optional default)
  (let* ((missing-key (gensym))
         (val (gethash key (car table) missing-key)))
    (if (eql missing-key val) default (cdr val))))

(defun keys (table)
  (let (key-id-pairs)
    (maphash
     (lambda (key val) (push (cons key (car val)) key-id-pairs))
     (car table))
    (mapcar #'car (sort key-id-pairs #'< :key #'cdr))))

(defvar *table* (make-ordered-hash-table))

(dotimes (i 10)
  (setkey *table* (/ (1+ i)) i))

(keys *table*)
; => (1 1/2 1/3 1/4 1/5 1/6 1/7 1/8 1/9 1/10)

(setkey *table* (/ 2) 20)
(setkey *table* (/ 4) 40)
(remkey *table* (/ 6))

(keys *table*)
; => (1 1/3 1/5 1/7 1/8 1/9 1/10 1/2 1/4)


;;; @@PLEAC@@_5.7
;;; You can  easily create hash tables  using multiple values per  key. For this
;;; purpose you  just need to increase  the verbosity of the  used test function
;;; during hash table creation.

(defvar *employee-table* (make-hash-table :test #'equal))

(defvar *employee-list*
  '(("R&D"   "Bob"   "1000")
    ("R&D"   "Trudy" "1001")
    ("R&D"   "Alice" "1002")
    ("Sales" "Bob"   "2001")
    ("Sales" "Jane"  "2003")))

;;; Pay attention that  we have to "Bob"s in  different departments. We'll place
;;; employees into  the `*EMPLOYEE-TABLE*' using  these two (name  & department)
;;; identifier fields.

(dolist (employee *employee-list*)
  (destructuring-bind (dept name phone) employee
    (setf (gethash (list dept name) *employee-table*)
          phone)))

(maphash (lambda (key val) (format t "~S => ~S~%" key val))
         *employee-table*)
; => ("R&D" "Bob")    => "1000"
;    ("R&D" "Trudy")  => "1001"
;    ("R&D" "Alice")  => "1002"
;    ("Sales" "Bob")  => "2001"
;    ("Sales" "Jane") => "2003"


;;; @@PLEAC@@_5.8
(defun reverse-hash-table (old-table &key test)
  (let ((new-table
         (make-hash-table
          :test (or test (hash-table-test old-table))
          :size (hash-table-size old-table))))
    (maphash (lambda (key val)
               (setf (gethash val new-table) key))
             old-table)
    new-table))

(defvar *table* (make-hash-table))

(dotimes (i 10) (setf (gethash (/ (1+ i)) *table*) i))

(maphash (lambda (key val) (format t "~S => ~S~%" key val))
         *table*)
; => 1    => 0
;    1/2  => 1
;    1/3  => 2
;    1/4  => 3
;    1/5  => 4
;    1/6  => 5
;    1/7  => 6
;    1/8  => 7
;    1/9  => 8
;    1/10 => 9

(maphash (lambda (key val) (format t "~S => ~S~%" key val))
         (reverse-hash-table *table*))
; => 0 => 1
;    1 => 1/2
;    2 => 1/3
;    3 => 1/4
;    4 => 1/5
;    5 => 1/6
;    6 => 1/7
;    7 => 1/8
;    8 => 1/9
;    9 => 1/10


;;; @@PLEAC@@_5.9
;;; One cannot (and shouldn't) make assumptions  on the order of the elements in
;;; a hash table in  Common Lisp. Anyway, while you're here, below  is a quick &
;;; dirty solution.

(defun ordered-keys (table)
  (sort
   (loop for key being each hash-key of table
         collect key)
   #'<))

(defvar *table* (make-hash-table))

(dotimes (i 10)
  (setf (gethash  (/ (1+ i)) *table*) i))

(ordered-keys *table*)
; => (1/10 1/9 1/8 1/7 1/6 1/5 1/4 1/3 1/2 1)


;;; @@PLEAC@@_5.10
(defun merge-hash-tables (&rest tables)
  (let ((union
         (make-hash-table
          :test (first
                 (sort (mapcar #'hash-table-test tables) #'>
                       :key (lambda (test)
                              (ecase test
                                (eq 0)
                                (eql 1)
                                (equal 2)
                                (equalp 3)))))
          :size (reduce #'max (mapcar #'hash-table-size tables)))))
    (dolist (table tables)
      (maphash (lambda (key val) (setf (gethash key union) val)) table))
    union))

(defvar *table-x* (make-hash-table))

(defvar *table-y* (make-hash-table :test #'equal))

(dotimes (i 5)
  (setf (gethash i *table-x*) (* 10 i)
        (gethash (format nil "~R" i) *table-y*) i))

(defvar *table-u* (merge-hash-tables *table-x* *table-y*))

(maphash (lambda (key val) (format t "~10S => ~S~%" key val)) *table-u*)
; => 0       => 0
;    1       => 10
;    2       => 20
;    3       => 30
;    4       => 40
;    "zero"  => 0
;    "one"   => 1
;    "two"   => 2
;    "three" => 3
;    "four"  => 4


;;; @@PLEAC@@_5.11
;;; Before  going  into  the  details   about  how  to  find  intersections  and
;;; differences between two hash tables,  one should note the existence of below
;;; functions operating on lists.

;;; Function <a href="http://l1sp.org/cl/set-exclusive-or">SET-EXCLUSIVE-OR</a>, <a href="http://l1sp.org/cl/nset-exclusive-or">NSET-EXCLUSIVE-OR</a>
;;; Function <a href="http://l1sp.org/cl/set-difference">SET-DIFFERENCE</a>, <a href="http://l1sp.org/cl/nset-difference">NSET-DIFFERENCE</a>
;;; Function <a href="http://l1sp.org/cl/intersection">INTERSECTION</a>, <a href="http://l1sp.org/cl/nintersection">NINTERSECTION</a>
;;; Function <a href="http://l1sp.org/cl/union">UNION</a>, <a href="http://l1sp.org/cl/nunion">NUNION</a>

;;; While  a hash  table implementation  of above  set operations  would perform
;;; better  for large  data sets,  people  report that  above functions  perform
;;; better than hash tables for collections of size 20-30 elements. On the other
;;; hand, it's  obvious that  working with  lists is much  more suitable  to the
;;; nature of a lisp program.

;;; Simple & Clean Hash Table Intersection/Difference Implementations

(defun hash-table-keys (table)
  (loop for key being each hash-key of table
        collect key))

(defun key-intersection (u v)
  (intersection
   (hash-table-keys u) (hash-table-keys v)
   :test (hash-table-test u)))

(defun key-difference (u v)
  (set-difference
   (hash-table-keys u) (hash-table-keys v)
   :test (hash-table-test u)))


;;; Hairy Hash Table Intersection/Difference Implementations

(defun hairy-key-frequency (tables &key (test 'eql))
  (let ((frequency (make-hash-table :test test)))
    (dolist (table tables)
      (maphash
       (lambda (key val)
         (declare (ignore val))
         (incf (gethash key frequency 0)))
       table))
    frequency))

(defmacro with-table-test-consistency ((tables test) &body body)
  (alexandria:with-unique-names (table)
    `(let ((,test (hash-table-test (first ,tables))))
       (unless (every (lambda (,table) (eql (hash-table-test ,table) ,test))
                      (rest ,tables))
         (error "Inconsistent test functions! (Expecting ~S.)" ,test))
       ,@body)))

(defun hairy-key-frequency-filter (tables pred)
  (when tables
    (with-table-test-consistency (tables test)
      (let (accum)
        (maphash
         (lambda (key val) (when (funcall pred val) (push key accum)))
         (hairy-key-frequency tables :test test))
        accum))))

(defun hairy-key-intersection (&rest tables)
  (hairy-key-frequency-filter tables (lambda (val) (< 1 val))))

(defun hairy-key-difference (&rest tables)
  (hairy-key-frequency-filter tables (lambda (val) (< val 2))))


;;; Example Usage

(let ((u (make-hash-table))
      (v (make-hash-table)))
  (dolist (i '(1 2 3 4 5)) (setf (gethash i u) t))
  (dolist (i '(3 4 5 6 7)) (setf (gethash i v) t))
  (list :simple (list :intersection (key-intersection u v)
                      :difference   (key-difference u v))
        :hairy  (list :intersection (hairy-key-intersection u v)
                      :difference   (hairy-key-difference u v))))
; => (:SIMPLE (:INTERSECTION (5 4 3) :DIFFERENCE (2 1))
;     :HAIRY  (:INTERSECTION (5 4 3) :DIFFERENCE (7 6 2 1)))


;;; @@PLEAC@@_5.12
(defun consume-stream (stream &key (buffer-size 8192))
  (let ((buffer
         (make-sequence
          `(vector ,(stream-element-type stream))
          buffer-size)))
    (loop for pos = (read-sequence buffer stream)
          until (zerop pos) sum pos)))

(let* ((streams (mapcar #'open (list "/etc/passwd" "/etc/motd" "/etc/fstab"))))
  (unwind-protect
       (let ((table (make-hash-table)))
         ;; Place `(STREAM . PATHNAME)' pairs as table key and values.
         (dolist (stream streams)
           (setf (gethash stream table) (pathname stream)))
         ;; Let's find the sizes of the streams.
         (let (accum)
           (maphash
            (lambda (key val) (push (cons val (consume-stream key)) accum))
            table)
           accum))
    ;; Don't forget to close open file streams.
    (mapcar #'close streams)))
; => ((#P"/etc/fstab"  . 488)
;     (#P"/etc/motd"   . 351)
;     (#P"/etc/passwd" . 1232))


;;; @@PLEAC@@_5.13
;;; In Common Lisp,  you can create hash tables of  previously known sizes using
;;; `SIZE'             keyword             supplied            to             <a
;;; href="http://l1sp.org/cl/make-hash-table">`MAKE-HASH-TABLE'</a>.    (Moreover
;;; there are `REHASH-SIZE'  and `REHASH-THRESHOLD' keyword parameters available
;;; which  might  interest  you.) But  once  you  created  a hash  table,  these
;;; configurations  stay as  is;  in  other words  later  modifications are  not
;;; allowed.


;;; @@PLEAC@@_5.14
;;; See  `HAIRY-KEY-FREQUENCY'  in  "Hairy  Hash  Table  Intersection/Difference
;;; Implementations".


;;; @@PLEAC@@_5.15
;;; We construct  the family  tree (in s-expressions)  extracted from  the given
;;; parent-child relationship table.

(defvar *relations*
  '(("Cain"      . "Adam")
    ("Abel"      . "Adam")
    ("Seth"      . "Adam")
    ("Enoch"     . "Cain")
    ("Irad"      . "Enoch")
    ("Mehujael"  . "Irad")
    ("Methusael" . "Mehujael")
    ("Lamech"    . "Methusael")
    ("Jabal"     . "Lamech")
    ("Jubal"     . "Lamech")
    ("Tubalcain" . "Lamech")))

(defun construct-relation-tree (rels)
  (labels ((construct (parent)
             (cons parent
                   (mapcar #'construct
                           (mapcar #'car
                                   (remove parent rels
                                           :key #'cdr
                                           :test (complement #'string-equal)))))))
    (mapcar #'construct
            (remove-duplicates
             (mapcar #'cdr
                     (remove-if
                      (lambda (pair)
                        (find (cdr pair) rels :test #'string-equal :key #'car))
                      rels))
             :test #'string-equal))))

(construct-relation-tree *relations*)
; => (("Adam"
;      ("Cain"
;       ("Enoch"
;        ("Irad"
;         ("Mehujael" ("Methusael" ("Lamech" ("Jabal") ("Jubal") ("Tubalcain")))))))
;      ("Abel")
;      ("Seth")))

;;; In a similar way, we can  construct the dependency tree between C source and
;;; header files.

(defun parse-file-dependencies (pathname)
  (let (deps)
    (with-open-file (in pathname)
      (loop for line = (read-line in nil nil)
            while line
            do (ppcre:do-scans
                   (m-s m-e r-ss r-es
                    "\\s*#\\s*include\\s*[<\"]([^>\"]+)[>\"]" line)
                 (push (subseq line (elt r-ss 0) (elt r-es 0)) deps))))
    deps))

(defun construct-file-relations (pathnames)
  (mapcan
   (lambda (child)
     (mapcar (lambda (parent)
               (cons
                (format nil "~A.~A" (pathname-name child) (pathname-type child))
                parent))
             (parse-file-dependencies child)))
   pathnames))

(construct-file-relations
 '("/tmp/c/hello.c" "/tmp/c/hello.h" "/tmp/c/main.c"))
; => (("hello.c" . "hello.h")
;     ("hello.c" . "stdio.h")
;     ("main.c" . "hello.h")
;     ("main.c" . "stdlib.h")
;     ("main.c" . "stdio.h"))

(construct-relation-tree
 (construct-file-relations
  '("/tmp/c/hello.c" "/tmp/c/hello.h" "/tmp/c/main.c")))
; => (("hello.h" ("hello.c") ("main.c"))
;     ("stdlib.h" ("main.c"))
;     ("stdio.h" ("hello.c") ("main.c")))

;;; Actually, whole PLEAC  example was related with hash  tables in its original
;;; demonstration.  But  the more I  think about it,  the more I find  that list
;;; structures are  more suitable for  this kind of  a tasks. But it  is obvious
;;; that, `REMOVE-DUPLICATES' kind of  tricks will perform poorly when duplicate
;;; eliminations will consume the majority  of the computation power and in such
;;; a situation you'll inevitably need hash tables.


;;; @@PLEAC@@_5.16
(defstruct hash-branch (weight 0) (tree nil))

(defun hash-tree-add (branch path weight)
  (let ((branch (or branch (make-hash-branch))))
    (prog1 branch
      (incf (hash-branch-weight branch) weight)
      (when path
        (setf (hash-branch-tree branch)
              (or (hash-branch-tree branch)
                  (make-hash-table :test #'equal)))
        (setf (gethash (first path) (hash-branch-tree branch))
              (hash-tree-add
               (gethash (first path) (hash-branch-tree branch))
               (rest path) weight))))))

(defun hash-tree->list-tree (branch)
  (when branch
    (cons (hash-branch-weight branch)
          (let ((tree (hash-branch-tree branch))
                (accum))
            (when tree
              (maphash
               (lambda (key val)
                 (push (cons key (hash-tree->list-tree val)) accum))
               tree)
              accum)))))

(defun print-list-tree (branches &optional (depth 0))
  (dolist (branch (sort (copy-list branches) #'> :key #'second))
    (format t "~8D |" (second branch))
    (loop repeat depth do (format t "--"))
    (format t "-> ~A~%" (first branch))
    (print-list-tree (cddr branch) (1+ depth))))

(defun du-output->size-path-pair (string)
  (with-input-from-string (in string)
    (loop for line = (read-line in nil nil)
          while line
          collect (ppcre:register-groups-bind (size path)
                      ("^\(\[^\\s\]+\)\\s+\(\[^\\s\]+\)$" line)
                    (cons (parse-integer size)
                          (delete-if #'zerop (ppcre:split "/" path)
                                     :key #'length))))))

(defun du-output->list-tree (string)
  (let ((branch (make-hash-branch)))
    (dolist (size-path-pair (du-output->size-path-pair string))
      (hash-tree-add branch (cdr size-path-pair) (car size-path-pair)))
    (rest (hash-tree->list-tree branch))))

(du-output->list-tree
  (trivial-shell:shell-command "du -ab /tmp/c"))
; => (("tmp" 21026
;      ("c" 21026 ("hello.h" 71) ("hello.c" 128) ("main.c" 123) ("main" 9168)
;       ("out" 1212 ("stdout" 44) ("stderr" 246) ("debug" 256)))))

(print-list-tree
 (du-output->list-tree
  (trivial-shell:shell-command "du -ab /tmp/c")))
; =>    21026 |-> tmp
;       21026 |---> c
;        9168 |-----> main
;        1212 |-----> out
;         256 |-------> debug
;         246 |-------> stderr
;          44 |-------> stdout
;         128 |-----> hello.c
;         123 |-----> main.c
;          71 |-----> hello.h


;;; @@PLEAC@@_6.0
;;;-----------------------------
;; Note that the following do not modify STRING, which may be
;; different from how the Perl snippet works.
(use-package :cl-ppcre)         ; assumed by all of section 6.0's code
(scan pattern string)
(regex-replace pattern string replacement)
;;;-----------------------------
(scan "sheep" meadow)   ; Non-nil if MEADOW contains "sheep"
(not (scan "sheep" meadow)) ; Non-nil if MEADOW doesn't contain "sheep"
(regex-replace "old" meadow "new") ; Replace "old" with "new" in MEADOW
;;;-----------------------------
;; Fine bovines demand fine toreadors.
;; Muskoxen are a polar ovibovine species.
;; Grooviness went out of fashion decades ago.
;;;-----------------------------
;; Ovines are found typically in oviaries.
;;;-----------------------------
(when (scan (create-scanner "\\bovines?\\b" :case-insensitive-mode t)
                     meadow)
           (format t "Here be sheep!"))
;;;-----------------------------
(setf my-string "good food")
(setf my-string (regex-replace "o*" my-string "e"))
;;;-----------------------------
;; Not sure how to reproduce the same output.  The above
;; REGEX-REPLACE just prepends "e" every time (but I'm not
;; sure that's wrong).
;;;-----------------------------
(with-input-from-string (s "ababacaca
")
  (let ((match (scan-to-strings "(a|ba|b)+(a|ac)+" (read-line s))))
    (format t "~A~%" match)))
;; ababa
;;;-----------------------------
;;% echo ababacaca | 
;;    awk 'match($0,/(a|ba|b)+(a|ac)+/) { print substr($0, RSTART, RLENGTH) }'
;;ababacaca
;;;-----------------------------
;; Since there is no magic $_ variable in CL, using MY-STRING as an
;; example.
(register-groups-bind (num)
    ("(\\d+)" some-string)
  (format t "Found number ~A~%" num))
;;;-----------------------------
;; Again, MY-STRING is a placeholder for $_.
(defparameter *numbers* (mapcar #'parse-integer (all-matches-as-strings "(\\d+)" my-string)))
;;;-----------------------------
(defparameter *digits* "1234567890")
(defparameter *nonlap* (all-matches-as-strings "(\\d\\d\\d)" *digits*))
(defparameter *yeslap* (all-matches-as-strings "(?=\\d\\d\\d)" *digits*))
(format t "Non-overlapping:  ~{~A~^ ~}~%Overlapping:      ~{~A~^ ~}~%" 
        *nonlap* *yeslap*)
;; Non-overlapping:  123 456 789
;; Overlapping:             

;; Note that CL-PPCRE seems to treat ?= differently from Perl, hence
;; the lack of output for Overlapping.
;;;-----------------------------
;; CL-PPCRE doesn't support $` etc after a match (it does for
;; REGEX-REPLACE but that wouldn't work here.
;;;-----------------------------

;;; @@PLEAC@@_6.1
;;;-----------------------------
(setf dst (regex-replace "that" src "this"))
;;;-----------------------------
;; No difference from previous.
;;;-----------------------------
;; strip to basename
(defparameter *progname* (regex-replace "^.*/" (car *posix-argv*) ""))

;; Make All Words Title-Cased
;; Unfortunately \u and \L aren't supported by CL-PPCRE (AFAICT), but
;; CL does have built-in support for capitalization.
(defparameter *capword* (string-capitalize *word*))

;; /usr/man/man3/foo.1 changes to /usr/man/cat3/foo.1
(defparameter *catpage* (regex-replace "man(?=\\d)" *manpage* "cat"))
;;;-----------------------------
(defparameter *bindirs* '("/usr/bin" "/bin" "/usr/local/bin"))
(defparameter *libdirs* (mapcar #'(lambda (dir) (regex-replace "bin" dir "lib")) *bindirs*))
(format t "~{~A~^ ~}~%" *libdirs*)
;; /usr/lib /lib /usr/local/lib
;;;-----------------------------
(setf a (regex-replace-all "x" b "y"))  ; copy B and then change A
;; CL-PPCRE doesn't support returning the count of changed characters.
;;;-----------------------------

;;; @@PLEAC@@_6.2
;;;-----------------------------
(when (scan "^[A-Za-z]+$" var)
  ;; it is purely alphabetic
  )
;;;-----------------------------
(when (scan "^[^\\W\\d_]+$" var)
  ;; it is purely alphabetic
  )
;;;-----------------------------
;; This seems to work without setting the locale.  Not sure why but
;; there ya' go.
(let ((data '("silly" "façade" "coöperate" "niño" "Renée" "Molière" "hæmoglobin" "naïve" "tschüß"
              "random!stuff#here")))
  (loop for word in data
        do (if (scan "^[^\W\d_]+$" word)
               (format t "~A: alphabetic~%" word)
               (format t "~A: line noise~%" word))))
;;silly: alphabetic
;;façade: line noise
;;coöperate: alphabetic
;;niño: alphabetic
;;Renée: alphabetic
;;Molière: alphabetic
;;hæmoglobin: alphabetic
;;naïve: alphabetic
;;tschüß: alphabetic
;;random!stuff#here: line noise
;;;-----------------------------

;;; @@PLEAC@@_6.3
;;;-----------------------------
;; "\\S+"               ; as many non-whitespace bytes as possible
;; "[A-Za-z'-]+"        ; as many letters, apostrophes, and hyphens
;;;-----------------------------
;; "\\b([A-Za-z]+)\\b"            ; usually best
;; "\\s([A-Za-z]+)\\s"            ; fails at ends or w/ punctuation
;;;-----------------------------

;;; @@PLEAC@@_6.4
;;;-----------------------------
;; It makes more sense for this just to be a function in CL rather
;; than a separate "script".
(defun hostname->address (hostname)
  (format nil "~{~A~^.~}" 
          (or
           (ignore-errors (concatenate 'list
                                       (sb-bsd-sockets:host-ent-address
                                        (sb-bsd-sockets:get-host-by-name hostname))))
           (list "???"))))

(defun resname (stream)
  (let ((matcher (create-scanner 
                  "    (     # capture the hostname in $1
        (?:                 # these parens for grouping only
            (?! [-_]  )     # lookahead for neither underscore nor dash
            [\\w-] +        # hostname component
            \\.             # and the domain dot
        ) +                 # now repeat that whole thing a bunch of times
        [A-Za-z]            # next must be a letter
        [\\w-] +            # now trailing domain part
    )                       # end of $1 capture" :extended-mode t)))
    (iter (for line in-stream stream using 'read-line)
          (format t "~A"
                  (regex-replace-all 
                   matcher
                   line 
                   #'(lambda (target-string start end 
                                            match-start match-end 
                                            reg-starts reg-ends)
                       (declare (ignore start end reg-starts reg-ends))
                       (let ((hostname (subseq target-string match-start match-end)))
                         (concatenate 'string hostname " [" (hostname->address hostname) "]"))))))))
;;;-----------------------------
(regex-replace-all 
 (create-scanner 
  "                  # replace
  \\#                #   a pound sign
  (\\w+)             #   the variable name
  \\#                #   another pound sign"
  :extended-mode t)
 my-string                         ; using this instead of implicit $_
 #'(lambda (target-string start end match-start match-end reg-starts reg-ends)
     (declare (ignore start end reg-starts reg-ends))
     (let ((symb (string-upcase (subseq target-string (elt reg-starts 0) (elt reg-ends 0)))))
       (format nil "~A" (symbol-value (intern symb))))))
;;;-----------------------------
;; I'm not sure there's any way to do this in CL.  There's
;; no guarantee that a local variable hasn't been optimized
;; away, for example.  EVAL operates in the null lexical
;; environment, so can't be used for this purpose.
;;;-----------------------------

;;; @@PLEAC@@_7.0
;;;-----------------------------
(with-open-file (input "/usr/local/widgets/data")
  ;; No need for "die" like call here b/c WITH-OPEN-FILE will do it
  ;; automatically.
  (iter (for line in-stream input using 'read-line)
        (when (scan "blue" line)
          (format t "~A~%" line)))
  ;; No need for explicit CLOSE here b/c WITH-OPEN-FILE will do it
  ;; automatically.
  )
;;;-----------------------------
(let ((var *standard-input*))
  (mysub var logfile))
;;;-----------------------------
;; The Perl example here is showing the "object-oriented" style of
;; file manipulation.  In CL it isn't any different than the above.
;; However we will use this opportunity to demonstrate how to
;; "manually" open/close a file without WITH-OPEN-FILE.
(let ((input (open "/usr/local/widgets/data")))
  (iter (for line in-stream input using 'read-line)
        (setf line (chomp line))
        (when (scan "blue" line)
          (format t "~A~%" line)))
  ;; Don't do this, either use WITH-OPEN-FILE or use UNWIND-PROTECT as
  ;; illustrated later.
  (close line))
;;;-----------------------------
(unwind-protect 
    (progn
      (iter (for line in-stream *standard-input* using 'read-line)
            (unless (scan "\\d" line)
              (warn "No digit found.~%"))
            (format t "Read: ~A~%" line)))
  ;; Not normally a good idea to do the following; just matching the
  ;; Perl.
  (close *standard-output*))
;;;-----------------------------
;; No need for explicit die, OPEN will throw an exception.
(defparameter *logfile* (open "/tmp/log" :direction :output))
;;;-----------------------------
(close *fh*)                            ; no need for die()
;;;-----------------------------
(let ((*standard-output* *logfile*))    ; switch to *LOGFILE* for output
  (format t "Countdown initiated ...~%"))
;; return to original output
(format t "You have 30 seconds to reach minimum safety distance.~%")
;;;-----------------------------

;;; @@PLEAC@@_7.1
;;;-----------------------------
;; For reading is the default, no need for "<" or equivalent.  No need
;; for explicit die()-like call either.  Note also that you should use
;; WITH-OPEN-FILE instead of a raw OPEN wherever possible.
(defparameter *source* (open path)) 

(defparameter *sink* (open path :direction :output)) 
;;;-----------------------------
#+sbcl
(progn
  (defparameter *source* (sb-posix:open path sb-posix:o-rdonly))

  (defparameter *sink* (sb-posix:open path sb-posix:o-wronly)))
;;;-----------------------------
;; There is no equivalent of Perl's "object-oriented" file interface
;; (arguably, the standard mechanism is already object-oriented).
;;;-----------------------------
#+sbcl
(progn
  (defparameter *filehandle* (sb-posix:open name flags))
  (defparameter *filehandle* (sb-posix:open name flags perms)))
;;;-----------------------------
(defparameter *fh* (open path))
#+sbcl
(defparameter *fh* (sb-posix:open path sb-posix:o-rdonly))
;;;-----------------------------
(defparameter *fh* (open path :direction :output))
#+sbcl
(defparameter *fh* (sb-posix:open path (logior sb-posix:o-wronly
                                               sb-posix:o-trunc
                                               sb-posix:o-creat)
                                  #o600))
;;;-----------------------------
#+sbcl
(progn
  (defparameter *fh* (sb-posix:open path (logior sb-posix:o-wronly
                                                 sb-posix:o-excl
                                                 sb-posix:o-creat)))
  (defparameter *fh* (sb-posix:open path (logior sb-posix:o-wronly
                                                 sb-posix:o-excl
                                                 sb-posix:o-creat)
                                    #o600)))
;;;-----------------------------
(defparameter *fh* (open path :direction :output 
                              :if-exists :append
                              :if-does-not-exist :create))
#+sbcl
(progn
  (defparameter *fh* (sb-posix:open path (logior sb-posix:o-wronly
                                                 sb-posix:o-append
                                                 sb-posix:o-creat)))
  (defparameter *fh* (sb-posix:open path (logior sb-posix:o-wronly
                                                 sb-posix:o-append
                                                 sb-posix:o-creat)
                                    #o600)))
;;;-----------------------------
(defparameter *fh* (sb-posix:open path (logior sb-posix:o-wronly sb-posix:o-append)))
;;;-----------------------------
(defparameter *fh* (open path :direction :io :if-exists :overwrite))
#+sbcl
(defparameter *fh* (sb-posix:open path sb-posix:o-rdwr))
;;;-----------------------------
#+sbcl
(progn
  (defparameter *fh* (sb-posix:open path (logior sb-posix:o-rdwr
                                                 sb-posix:o-creat)))
  (defparameter *fh* (sb-posix:open path (logior sb-posix:o-rdwr
                                                 sb-posix:o-creat)
                                    #o600)))
;;;-----------------------------
#+sbcl
(progn
  (defparameter *fh* (sb-posix:open path (logior sb-posix:o-rdwr
                                                 sb-posix:o-excl
                                                 sb-posix:o-creat)))
  (defparameter *fh* (sb-posix:open path (logior sb-posix:o-rdwr
                                                 sb-posix:o-excl
                                                 sb-posix:o-creat)
                                    #o600)))
;;;-----------------------------

;;; @@PLEAC@@_7.2
;;;-----------------------------
;; The machinations that the Perl example is doing is dealing with the
;; fact that Perl normally ignores leading whitespace in a filename.
;; This shouldn't be necessary in CL (since the filename doesn't also
;; contain the input mode, as it does in Perl), but the following
;; example illustrates how to do the same thing anyway.
(setf *filename* (regex-replace "^(\\s)" *filename* "./$1"))
;; I'm not sure what the \0 being appended in the Perl example is for,
;; but SBCL, at least, doesn't seem to even allow NUL in a namestring
;; (filename), so it's not shown here.
(defparameter *handle* (open *filename*))
;;;-----------------------------
#+sbcl
(defparameter *handle* (sb-posix:open *filename* sb-posix:o-rdonly))
;;;-----------------------------
(defparameter *filename* (second *posix-argv*))
(defparameter *input* (open *filename*))
;;;-----------------------------
(defparameter *output* (open *filename* :direction :output))
;;;-----------------------------
#+sbcl
(defparameter *output* (sb-posix:open *filename* (logior sb-posix:o-wronly 
                                                         sb-posix:o-trunc)))
;;;-----------------------------
(setf *file* (regex-replace "^(\\s)" *file* "./$1"))
(defparameter *output* (open *file* :direction :output))
;;;-----------------------------

;;; @@PLEAC@@_7.3
;;;-----------------------------
;;; @@INCOMPLETE@@
;;; @@INCOMPLETE@@


;;; @@PLEAC@@_7.4
;;;-----------------------------

;; You should not normally do this in CL.  However the example below
;; does roughly the same thing as the Perl and is a crude example of
;; how you can handle exceptions in CL.
(handler-case
    (let ((file (open *path*)))
      ;; use FILE
      )
  ;; Catch "all" exceptions (CONDITION is the base class of all
  ;; "exceptions" in CL).
  (condition (msg)
    (format *error-output* "~&Couldn't open ~A for reading : ~A~%" *path* msg)))
;;;-----------------------------

;;; @@PLEAC@@_7.6
;;;-----------------------------
(defparameter *data* "
Your data goes here
")

(loop for line in (split #\Newline *data*)
     do
     (progn
       ;; process the line
       ))
;;;-----------------------------
;; The Perl example here would be the same as the above.
;;;-----------------------------
;; There's no equivalent to how DATA is used here.  E.g., there's no
;; standard way to get the currently executing "script" file.
;;;-----------------------------

;;; @@PLEAC@@_7.18
;;;-----------------------------
(loop
   for filehandle in *filehandles* ; *FILEHANDLES* is list of STREAM objects
   do (princ stuff-to-print filehandle))
;;;-----------------------------
;;; @@INCOMPLETE@@


;;; @@PLEAC@@_8.2
;;;-----------------------------
;; Should we count the last line, if it does not end with a newline?
;; This version counts:
(with-open-file (stream #p"numbers.html")
  (loop for line = (read-line stream nil)
        while line
        count t))
;; and this does not:
(with-open-file (stream #p"numbers.html")
  (loop for (line missing-newline-p) =
            (multiple-value-list (read-line stream nil))
        while line
        count (not missing-newline-p)))
;;; @@INCOMPLETE@@

;;; @@PLEAC@@_10.0
;;;-----------------------------
(defparameter *greeted* 0)              ; global variable
(defun hello ()
  (incf *greeted*)
  (format t "hi there!~%"))
;;;-----------------------------
(hello)           ; call subroutine hello with no arguments/parameters
;;;-----------------------------

;;; @@PLEAC@@_10.1
;;;-----------------------------
;; It would be strange to declare arguments using &rest when you know
;; there are exactly two, in CL, but you could, if you wanted to
;; emulate what the Perl example does.
(defun hypotenuse (&rest args)
  (sqrt (+ (expt (elt args 0) 2) 
           (expt (elt args 1) 2))))

(setf diag (hypotenuse 3 4))            ; DIAG is 5.0
;;;-----------------------------
(defun hypotenuse (side1 side2)
  (sqrt (+ (expt side1 2) 
           (expt side2 2))))
;;;-----------------------------
(format t "~D~%" (truncate (hypotenuse 3 4))) ; prints 5

(let ((a '(3 4)))
  (format t "~D~%" (truncate (apply 'hypotenuse a)))) ; prints 5
;;;-----------------------------
(setf both (append men women))
(setf both `(,@men ,@women)) ; alternative way of doing the same thing
;;;-----------------------------
(setf nums '(1.4 3.5 6.7))
(setf ints (apply 'int-all nums))       ; NUMS unchanged
(defun int-all (&rest retlist)
  (loop for n in retlist collect (truncate n)))
;;;-----------------------------
(setf nums '(1.4 3.5 6.7))
(trunc-em nums)                         ; NUMS now (1 3 6)
(defun trunc-em (reals)
  (map-into reals 'truncate reals))     ; truncate each element of arg list
;;;-----------------------------

;;; @@PLEAC@@_10.2
;;;-----------------------------
(defun somefunc ()
  (let (variable              ; VARIABLE is invisible outside SOMEFUNC
        another an-array a-hash)    ; declaring many variables at once
    ;; ...
    ))
;;;-----------------------------
(destructuring-bind (name age) *posix-argv*
  ;; Use NAME, AGE here
  )

(setf start (fetch-time))
;;;-----------------------------
(destructuring-bind (a b) pair
  (let ((c (fetch-time)))
    ;; ...
    ))

(defun check-x (x)
  (let ((y "whatever"))
    (run-check)
    (when condition
      (format t "got ~A~%" x))))
;;;-----------------------------
(defun save-array (&rest arguments)
  ;; There's probably a better way to do this.
  (setf *global-array* (append *global-array* (copy-seq arguments))))
;;;-----------------------------

;;; @@PLEAC@@_10.3
;;;-----------------------------
(let (variable)
  (defun mysub ()
    ;; ... accessing VARIABLE
    ))
;;;-----------------------------
(let ((variable 1))
  (defun othersub ()
    ;; ... accessing VARIABLE
    ))
;;;-----------------------------
(let ((counter 0))
  (defun next-counter ()
    (incf counter)))
;;;-----------------------------
(let ((counter 42))
  (defun next-counter ()
    (incf counter))
  (defun prev-counter ()
    (decf counter)))
;;;-----------------------------

;;; @@PLEAC@@_10.4
;;;-----------------------------
;; There is no standard equivalent of Perl's caller(), in CL.
;; Functions can get inlined (among other things), so it's not even
;; clear what something like caller() should actually return, anyway.
;;;-----------------------------

;;; @@PLEAC@@_10.5
;;;-----------------------------
(array-diff array1 array2)              ; params are already references
;;;-----------------------------
(setf a #(1 2))
(setf b #(5 8))
(setf c (add-vecpair a b))
(format t "~{~A~^ ~}~%" (map 'list 'identity c))
;; 6 10

;; This function would be simpler with lists instead of arrays, or the
;; use of the SERIES package.  We're using arrays because the Perl
;; does.
(defun add-vecpair (x y)        ; assumes both vectors the same length
  (map-into (make-array (length x))
            '+ x y))
;;;-----------------------------
;; Normally one would use CHECK-TYPE or ASSERT here, but this example
;; is trying to match the Perl.
(unless (and (typep x 'vector)
             (typep y 'vector))
  (error "usage: add_vecpair VECTOR1 VECTOR2"))
;;;-----------------------------

;;; @@PLEAC@@_10.6
;;;-----------------------------
;; There is no equivalent to Perl's wantarray() in CL.  The most
;; similar language feature is CL's ability to return multiple values,
;; which the caller may choose to ignore.
;;;-----------------------------

;;; @@PLEAC@@_10.7
;;;-----------------------------
(thefunc :increment "20s" :start "+5m" :finish "+30m")
(thefunc :start "+5m" :finish "+30m")
(thefunc :finish "+30m")
(thefunc :start "+5m" :increment "15s")
;;;-----------------------------
;; &allow-other-keys is used to emulate the Perl example's use of @_
;; in the %args hash.
(defun thefunc (&key (increment "10s") finish start &allow-other-keys)
  (when (scan "m$" increment)
    ;; ...
    ))
;;;-----------------------------

;;; @@PLEAC@@_10.8
;;;-----------------------------
;; Use of gensym here is unusual, just trying to mimic the Perl (there
;; is probably a better way to do that, too).  Also, normally you'd do
;; MULTIPLE-VALUE-BIND.
(multiple-value-setq (a #.(gensym) c) (func))
;;;-----------------------------
;; I don't know of a quicker built-in way to do exactly what the Perl
;; is doing here.  There is NTH-VALUE but it only returns one value.
(let ((results (multiple-value-list (func))))
  (setf a (elt results 0)
        c (elt results 2)))

;; However you can easily define a macro that does roughly the same
;; thing.
(defmacro nth-values ((&rest positions) &body body)
  (let ((results (gensym "results-")))
    `(let ((,results (multiple-value-list ,@body)))
       (values
        ,@(mapcar #'(lambda (pos) `(elt ,results ,pos)) positions)))))

(multiple-value-setq (a c) (nth-values (0 2) (func)))
;;;-----------------------------
#+sbcl
(multiple-value-setq (dev ino dummy dummy uid)  (sb-unix:unix-stat filename))
;;;-----------------------------
#+sbcl
(multiple-value-setq (dev ino #.(gensym) #.(gensym) uid)  (sb-unix:unix-stat filename))
;;;-----------------------------
;; Using the non-standard NTH-VALUES macro defined above.
#+sbcl
(multiple-value-setq (dev ino uid gid) (nth-values (0 1 4 5) (sb-unix:unix-stat filename)))
;;;-----------------------------

;;; @@PLEAC@@_10.9
;;;-----------------------------
(multiple-value-setq (array hash) (somefunc))

(defun somefunc ()
  (let ((array (make-array ...))
        (hash (make-hash-table ...)))
    ;; ...
    (values array hash)))
;;;-----------------------------
(defun fn ()
  ;; ...
  (values a b c))                 ; assuming a, b and c are all hashes
;;;-----------------------------
(multiple-value-setq (h0 h1 h2) (fn))   ; unlike Perl example, not "wrong"
(setf list-of-hashes (multiple-value-list (fn))) ; eg: (gethash "keystring" (elt list-of-hashes 2))
(multiple-value-setq (r0 r1 r2) (fn)) ; everything's a reference, no difference from previous
;;;-----------------------------

;;; @@PLEAC@@_10.10
;;;-----------------------------
;; In CL everything returns a value.
;;;-----------------------------
(defun empty-retval ())                 ; returns nil
;; If you want to distinguish between returning "empty" vs "undefined"
;; then you can return return a second value indicating which.
(defun empty-retval ()
  (values nil nil))
;;;-----------------------------
(let ((a (yourfunc)))
  (when a
    ;; ...
    ))
;;;-----------------------------
;; The following are all the same, just mirroring the Perl here.
(let ((a (sfunc)))
  (unless a
    (error "sfunc failed")))

(let ((a (afunc)))
  (unless a
    (error "afunc failed")))

(let ((a (hfunc)))
  (unless a
    (error "hfunc failed")))
;;;-----------------------------
;; Note: this is for illustrating the use of OR and ERROR, there is no
;; built-in ioctl or strerror in CL.
(or (ioctl ...) (error "can't ioctl: ~A" strerror))
;;;-----------------------------

;;; @@PLEAC@@_10.11
;;;-----------------------------
(setf results (myfunc 3 5))
;;;-----------------------------
;; Unlike Perl, you can't call functions without using outer parens
;; (unless you develop macros to let you do so in specific
;; circumstances)
(setf results (myfunc 3 5))
;;;-----------------------------
;;;-----------------------------
(setf results `(,@(myfunc 3) 5))
;;;-----------------------------
(defun lock-sh () 1)
(defun lock-ex () 2)
(defun lock-un () 4)
;;;-----------------------------
(defun mypush (list &rest remainder)
  ;; ...
  )
;;;-----------------------------
(mypush (if (> x 10) a b) 3 5)          ; unlike Perl, not wrong
;;;-----------------------------
;; Params are already passed as references in CL
;;;-----------------------------
(defun hpush (href &rest keys-and-values)
  (loop
     for k in keys-and-values by #'cddr
     for v in (cdr keys-and-values) by #'cddr
     do (setf (gethash k href) v))
  href)                                 ; return this for caller's convenience

(hpush pieces "queen" 9 "rook" 5)
;;;-----------------------------

;;; @@PLEAC@@_10.12
;;;-----------------------------
(error "some message")                  ; raise exception
;;;-----------------------------
(multiple-value-bind (result condition)
    (ignore-errors (eval (func)))
  (when condition (warn "func raised an exception: ~A" condition)))
;;;-----------------------------
(multiple-value-bind (result condition)
    (ignore-errors (eval (setf val (func))))
  (when condition (warn "func blew up: ~A" condition)))
;;;-----------------------------
(multiple-value-bind (result condition)
    (ignore-errors (eval (setf val (func))))
  (when condition (warn "func blew up: ~A" condition)))
;;;-----------------------------
(multiple-value-bind (result condition)
    (ignore-errors (eval (setf val (func))))
  (when (and condition
             (not (scan "Full moon" 
                                 ;; There's probably a better way to
                                 ;; do this.
                                 (format nil "~A" condition))))
    (warn "func blew up: ~A" condition)))
;;;-----------------------------
;; No equivalent to wantarray().
;;;-----------------------------

;;; @@PLEAC@@_10.13
;;;-----------------------------
(defparameter *age* 18)                 ; global variable
(when CONDITION
  (let ((*age* 23))
    (func)                              ; sees temporary value of 23
    )) ; restore old value at block exit
;;;-----------------------------
(setf para (get-paragraph fh))
(defun get-paragraph (fh)
  ;; Skip leading newlines.
  (loop for peek = (peek-char nil fh nil nil)
     while (and peek (eql peek #\Newline))
     do (read-char fh nil nil))
  (chomp
   (coerce (loop 
              for c = (read-char fh nil :eof)
              until (or (eq c :eof)
                        (and (eql c #\Newline)
                             (eql (peek-char nil fh nil #\Newline)
                                  #\Newline)))
              collect c)
           'string)))
;;;-----------------------------
(setf contents (get-motd))
(defun get-motd ()
  (with-open-file (motd "/etc/motd") ; will do die()-like stuff automatically
    (coerce (loop
               for c = (read-char motd nil :eof)
               until (eq c :eof)
               collect c)
            'string)))
;;;-----------------------------
;;;-----------------------------
;; Note: in the spirit of the Perl, this section should be done using
;; LET and DECLARE SPECIAL but I couldn't get that to work.
(defparameter *nums* '(0 1 2 3 4 5))

(defun my-second ()     ; don't redefine CL's standard SECOND function
  (format t "~{~A~^ ~}~%" *nums*))

(defun my-first ()
  (let ((*nums* (copy-list *nums*)))
    (setf (elt *nums* 3) 3.14159)
    (my-second)))

(my-second)
;; 0 1 2 3 4 5
(my-first)
;; 0 1 2 3.14159 4 5
;;;-----------------------------
;; No obvious equivalent to %SIG
;;;-----------------------------

;;;-----------------------------
;;; @@INCOMPLETE@@

;;; @@PLEAC@@_10.14
;;;-----------------------------
(fmakunbound 'grow) ; not sure this is necessary, but more like the Perl
(setf (symbol-function 'grow) #'expand)
(grow)                                  ; calls EXPAND
;;;-----------------------------
(setf one:var two:table)                ; make ONE:VAR alias for TWO:TABLE
(setf (symbol-function 'one:big) #'two:small) ; make ONE:BIG alias for TWO:SMALL
;;;-----------------------------
(let ((fred #'barney))              ; temporarily alias FRED to BARNEY
  ;; ...
  )
;;;-----------------------------
(setf string (red "careful here"))
(format t "~A" string)
;; <FONT COLOR='red'>careful here</FONT>
;;;-----------------------------
(defun red (string)
  (concatenate 'string "<FONT COLOR='red'>" string "</FONT>"))
;;;-----------------------------
(defmacro color-font (color)
  `(defun ,(intern (string-upcase color)) (string)
     (concatenate 'string "<FONT COLOR='" ,color "'>" string "</FONT>")))

(color-font "red")
(color-font "green")
(color-font "blue")
(color-font "purple")
;; etc
;;;-----------------------------
(defmacro color-fonts (&rest colors)
  (append '(progn)
          (loop for color in colors
             collect `(color-font ,color))))

(color-fonts "red" "green" "blue" "yellow" "orange" "purple" "violet")
;;;-----------------------------

;;; @@PLEAC@@_10.16
;;;-----------------------------
(defun outer (arg)
  (let* ((x (+ arg 35))
         ;; You're much less likely to do this accidentally in CL, but
         ;; I'm trying to match the spirit of the Perl example.
         (inner (block nil
                  (return (* x 19)))))  ; WRONG
    (+ x (inner))))
;;;-----------------------------
(defun outer (arg)
  (let ((x (+ arg 35)))
    (flet ((inner () (* x 19)))
      (+ x (inner)))))
;;;-----------------------------

;;; @@PLEAC@@_10.17
;;;-----------------------------

(defgeneric cmp (a b)
  (:documentation "Vaguely like Perl's cmp() function."))

(defmethod cmp ((a string) (b string))
  (cond
    ((string= a b) 0)
    ((string-lessp a b) -1)
    (t 1)))

(defmethod cmp ((a number) (b number))
  (cond
    ((= a b) 0)
    ((< a b) -1)
    (t 1)))

(defmethod cmp (a b)
  0)

(defun bysub1 (&rest filenames)
  (let ((sub (make-array 0 :fill-pointer 0))
        (msgs (make-array 0 :fill-pointer 0)))
    (dolist (filename filenames)
      (with-open-file (file filename)
        ;; GET-PARAGRAPH defined in section 10.13
        (loop
           for paragraph = (get-paragraph file)
           until (string-equal paragraph "")
           do (when (scan (create-scanner #?r"^From" :multi-line-mode t) 
                          paragraph)
                (vector-push-extend
                 (or
                  (register-groups-bind (subject)
                      ((create-scanner #?r/^Subject:\s*(?:Re:\s*)*(.*)/
                                       :case-insensitive-mode t :multi-line-mode t)
                       paragraph)
                    (string-downcase subject))
                  "")
                 sub))
           (vector-push-extend paragraph msgs))))
    (let ((indices (make-array (length msgs)
                               :initial-contents (loop
                                                    for i below (length msgs) 
                                                    collect i))))
      (sort indices #'(lambda (a b)
                        (case (if (and (< a (length sub)) (< b (length sub)))
                                  (cmp (aref sub a) (aref sub b))
                                  0)
                          (0 (< a b))
                          (-1 t))))
      (map nil #'(lambda (i)
                   (format t "~A~%" (aref msgs i)))
           indices))))

;; bysub2 illustrates a Perl-specific idiom and will be skipped.

(defun print-hash-table (hashtable)
  "Useful for debugging."
  (loop
     for key being the hash-keys of hashtable using (hash-value value)
     do (format t "~A: ~A~%" key value)))
       
(defun bysub3 (&rest filenames)
  (let ((msgs (make-array 0 :fill-pointer 0)))
    (dolist (filename filenames)
      (with-open-file (file filename)
        (loop
           for paragraph = (get-paragraph file)
           until (string-equal paragraph "")
           do 
           (when (scan (create-scanner #?r"^From" :multi-line-mode t) 
                       paragraph)
             (vector-push-extend
              (mkhash                   ; MKHASH defined in appendix
               :subject (register-groups-bind (subject)
                            ((create-scanner #?r/^Subject:\s*(?:Re:\s*)*(.*)/
                                             :case-insensitive-mode t :multi-line-mode t) paragraph)
                          (string-downcase subject))
               :number (fill-pointer msgs)
               :text "")
              msgs))
           (let ((mail-record (aref msgs (1- (fill-pointer msgs)))))
             (setf (gethash :text mail-record) (concatenate 'string (gethash :text mail-record) paragraph))))))
    (map nil #'(lambda (msg)
                 (format t "~A" (gethash :text msg)))
         (sort msgs #'(lambda (a b)
                        (let ((subject-a (gethash :subject a))
                              (subject-b (gethash :subject b)))
                          (case (cmp subject-a subject-b)
                            (0 (< (gethash :number a) (gethash :number b)))
                            (-1 t))))))))

;; Can be downloaded using ASDF-INSTALL
(require :metatilities)

(defun datesort (&rest filenames)
  (let ((msgs (make-array 0 :fill-pointer 0)))
    (dolist (filename filenames)
      (with-open-file (file filename)
        (loop
           for paragraph = (get-paragraph file)
           until (string-equal paragraph "")
           do 
           (when (scan (create-scanner #?r"^From" :multi-line-mode t) 
                       paragraph)
             (vector-push-extend
              (mkhash
               :subject (register-groups-bind (subject)
                            ((create-scanner #?r/^Subject:\s*(?:Re:\s*)*(.*)/
                                             :case-insensitive-mode t :multi-line-mode t) paragraph)
                          (string-downcase subject))
               :number (fill-pointer msgs)
               ;; Need IGNORE-ERRORS because PARSE-DATE-AND-TIME can
               ;; signal conditions
               :date (ignore-errors 
                       (metatilities:parse-date-and-time
                        (register-groups-bind (date)
                            ((create-scanner #?r/^Date:\s*(.*)/ :multi-line-mode t) paragraph)
                          (car (split #?r"\s+\(" date)))))
               :text "")
              msgs))
           (let ((mail-record (aref msgs (1- (fill-pointer msgs)))))
             (setf (gethash :text mail-record) (concatenate 'string (gethash :text mail-record) paragraph))))))
    (map nil #'(lambda (msg)
                 (format t "~A" (gethash :text msg)))
         (sort msgs #'(lambda (a b)
                        (case (cmp (gethash :subject a) (gethash :subject b))
                          (-1 t)
                          (0 (case (cmp (gethash :date a) (gethash :date b))
                               (-1 t)
                               (0 (< (gethash :number a) (gethash :number b)))))))))))

;;; @@PLEAC@@_11.0
;;;-----------------------------
;; In CL you don't need extra syntax to treat variables (symbols) as
;; references, they already work that way.
(format t "~A" sref) ; prints the value that the reference SREF refers to
(setf sref 3)                           ; assigns SREF's referent
;;;-----------------------------
;; The Perl subsection here isn't any different from the above, in CL.
;;print ${$sref};             # prints the scalar $sref refers to
;;${$sref} = 3;               # assigns to $sref's referent
;;;-----------------------------
;; We're calling this MY-AREF instead of AREF to avoid confusion with
;; CL's built-in AREF function.
(setf my-aref array)       ; no special synatx needed to get reference
;;;-----------------------------
;; Not sure what the Perl here is trying to show.  Probably has no
;; realistic equivalent in CL.
;; $pi = \3.14159;
;; $$pi = 4;           # runtime error
;;;-----------------------------
(setf my-aref #(3 4 5)) ; new array (no "anonymous" distinction in CL)
;; MKHASH defined in appendix, not standard CL
(setf href (mkhash "How" "Now" "Brown" "Cow"))
;;;-----------------------------
(makunbound 'my-aref)
(setf my-aref #(1 2 3))
(format t "~A" my-aref)
;; #(1 2 3)
;;;-----------------------------
;; Perl doesn't support "rectangular" multi-dimensional arrays (i.e.,
;; a continuous block of memory with all cells preallocated), instead
;; it supports "jagged" arrays (i.e., arrays of references to other
;; arrays).  CL supports rectangular arrays by default, but there's
;; nothing stopping you from using jagged arrays instead, since CL
;; arrays can contain anything.  In this example we'll assume jagged
;; arrays, to match the Perl example's semantics.

;; You could go like this, but it is a lot of typing and is confusing
;; to read.
(setf (aref (aref (aref (aref a 4) 23) 53) 21) "fred")
(format t "~A" (aref (aref (aref (aref a 4) 23) 53) 21))
;; fred

;; An AREF-like macro to handle jagged arrays will save typing/errors.
(defmacro perl-aref (array &rest subscripts)
  "Allows AREF-like access to arrays-of-refrences (as opposed to true
multidimensional arrays.)"
  (labels ((make-arefs (array subscripts)
             (if subscripts
                 (make-arefs `(aref ,array ,(car subscripts)) (cdr subscripts))
                 array)))
    (make-arefs array subscripts)))

(setf (perl-aref a 4 23 53 21) "fred")
(format t "~A" (perl-aref a 4 23 53 21))
;; fred

;; Each of the following will print out the entire substructure.
(format t "~A" (perl-aref a 4 23 53))
(format t "~A" (perl-aref a 4 23))
(format t "~A" (perl-aref a 4))
;;;-----------------------------
(setf op-cit (or (cite ibid) (error "couldn't make a reference")))
;;;-----------------------------
;; MKHASH defined in appendix
(setf nat (mkhash "Name" "Leonhard Euler"
                  "Address" (format nil "1729 Ramanujan Lane~%Mathworld, PI 31416")
                  "Birthday" #x5bb5580))
;;;-----------------------------

;;; @@PLEAC@@_11.1
;;;-----------------------------
(setf my-aref array) 
(setf anon-array #(1 3 5 7 9))
(setf anon-copy (copy-seq my-array))
(setf implicit-creation (copy-seq #(2 4 6 8 10))) ; not sure this is what the Perl means
;;;-----------------------------
(vector-push-extend 11 anon-array)      ; ANON-ARRAY must have fill pointer (unlike above)
;;;-----------------------------
(setf two (aref implicit-creation 0))
;;;-----------------------------
(setf last-idx (1- (length my-aref)))
(setf num-items (length my-aref))
;;;-----------------------------
;; check wehther SOMEREF contains a simple array reference
(check-type someref simple-vector) ; CHECK-TYPE does a die() implicitly, if necessary

(format t "~{~A~^ ~}~%" (coerce array-ref 'list))

;; SORT modifies the original array so we use STABLE-SORT to be more
;; like the Perl example.
(setf order (stable-sort array-ref '<))

;; Only works if ARRAY-REF has a fill-pointer
(setf array-ref (make-array 0 :adjustable t :fill-pointer 0)) ; for example
(vector-push-extend item array-ref)
;;;-----------------------------
(defun array-ref ()
  ;; This is probably the closest to what the Perl would return.
  (make-array 0 :adjustable t :fill-pointer 0))

(setf aref1 (array-ref))
(setf aref2 (array-ref))
;;;-----------------------------
(format t "~A" (aref array-ref n))      ; access item in position N, works on any array
(format t "~A" (svref array-ref n)) ; access item in position N, possibly fastest, only
                                    ; works on type SIMPLE-VECTOR (single-dimensional arrays)
(format t "~A" (elt array-ref n))       ; same, works on any sequence type, but possibly slower
;;;-----------------------------
(setf pie #(0 1 2 3 4 5 6 7 8 9))
(make-array 3 :displaced-to pie :displaced-index-offset 3) ; array slice
;;;-----------------------------
(setf (subseq pie 3 6) #("blackberry" "blueberry" "pumpkin")) ; note 6 instead of 5, not a typo
;;;-----------------------------
(setf sliceref (make-array 3 :displaced-to pie :displaced-index-offset 3)) ; not wrong
;;;-----------------------------
(map nil
     #'(lambda (item)
         ;; ITEM has data
         )
     array-ref)

(dotimes (idx (array-dimension array-ref 0))
  ;; (svref array-ref idx) has data
  )
;;;-----------------------------

;;; @@PLEAC@@_11.2
;;;-----------------------------
;; Note: HASH must be creaed with :TEST 'EQUAL
(push "new value" (gethash "KEYNAME" hash))
;;;-----------------------------
(loop
   for string being the hash-keys of hash
   do (format t "~A: ~A~%" string (gethash string hash)))
;;;-----------------------------
(setf (gethash "a key" hash) #(3 4 5))  ; anonymous array
(setf (gethash "a key" hash) '(3 4 5))  ; ...or a list would work too
;;;-----------------------------
(setf values (gethash "a key" hash))
;;;-----------------------------
(push value (gethash "a key" hash))
;;;-----------------------------
(setf residents (gethash number phone2name)) ; will be NIL if not found
;;;-----------------------------
;; The Perl example would translate to the same thing as the previous
;; subsection in CL (since GETHASH returns NIL when there is no value,
;; and NIL is the empty list).  However, to match the "sprit" of the
;; Perl example (and return an empty array instead of an empty list),
;; you could do the following, which takes advantage of the fact that
;; GETHASH returns a second value indicating whether or not the hash
;; key actually has a value.
(setf residents (multiple-value-bind (value exists) (gethash number phone2name)
                  (if exists
                      value
                      #())))
;;;-----------------------------


;;; @@PLEAC@@_11.3
;;;-----------------------------
(setf href hash)
(setf anon-hash (mkhash "key1" "value1" "key2" "value2" ...)) ; MKHASH defined in appendix

;; Couldn't find anything like this in standard CL.  Someone please
;; correct me if I'm wrong.
(defun copy-hash-table (hash-table)
  "Make shallow copy of HASH."
  (let ((newhash (make-hash-table :test (hash-table-test hash-table) 
                                  :size (hash-table-size hash-table))))
    (loop for key being the hash-keys of hash-table using (hash-value value)
         do (setf (gethash key newhash) (gethash key hash-table)))
    newhash))

(setf anonymous-hash-copy (copy-hash-table hash))
;;;-----------------------------
(setf hash href)
(setf value (gethash key href))

(setf slice (loop for key in (list key1 key2 key3)
                 collect (gethash key href)))
(setf keys (loop for key being the hash-keys of href collect key))
;;;-----------------------------
(check-type someref hash-table) ; CHECK-TYPE does a die() implicitly, if necessary
;;;-----------------------------
(dolist (href (list env inc)) ; ENV and INC don't exist in CL, just matching the Perl
  (loop for key being the hash-keys of href using (hash-value value)
       do (format t "~A => ~A~%" key value)))
;;;-----------------------------
(setf values (loop for key in '("key1" "key2" "key3")
                collect (gethash key hash-ref)))

;; The following will NOT work like the Perl example, VAL is a copy of
;; the hash value because numeric values are copied.
(dolist val (loop for key in '("key1" "key2" "key3")
               collect (gethash key hash-ref))
        (incf val 7))              ; does NOT modify hash table at all

;; You'd have to do something like this instead.
(loop for key in '("key1" "key2" "key3")
   do (incf (gethash key hash-ref 0) 7))
;;;-----------------------------
        
;;; @@PLEAC@@_11.4
;;;-----------------------------
;; If you want to be able to call the function using the alias like
;; "normal" (i.e., as the first element of a form) SETF its
;; SYMBOL-FUNCTION:
(setf (symbol-function 'cref) #'func)
(setf (symbol-function 'cref) #'(lambda (...)))

;; If you do the following instead, you'll have to use APPLY and/or
;; FUNCALL.
(setf 'cref2 #'func)
(setf 'cref2 #'(lambda (...)))
;;;-----------------------------
(setf returned (cref ...))
;; If you have a list of arguments (more like the Perl example):
(setf returned (apply 'cref arguments))
;; Or you can use FUNCALL (not that you really need to in this case)
(setf returned (funcall 'cref ...))

;; If you didn't use SYMBOL-FUNCTION, then you can do the following:
(setf returned (apply cref arguments)) ; note the lack of a ' in front of CREF
(setf returned (funcall cref ...))      ; ditto
;;;-----------------------------
(defun thefunc ()
  ;; ...
  )
(setf funcname "THEFUNC")  ; upper-case to pick up correct symbol name
(funcall (intern funcname))
;;;-----------------------------
;; MKHASH defined in appendix
(defparameter *commands*
  (mkhash "happy" #'joy
          "sad" #'sullen
          "done" #'(lambda () (error "See ya!"))
          "mad" #'angry))

(format t "How are you?")
(let* ((string (chomp (read-line))) ; CHOMP defined in appendix
       (command (gethash string *commands*)))
  (if command
      (funcall command)
      (format t "No such command: ~A~%" string)))
;;;-----------------------------
(defun counter-maker ()
  (let ((start 0))
    #'(lambda ()
        (prog1 start                       ; return value of START prior to increment
          (incf start)))))

(setf (symbol-function 'counter) (counter-maker))

(loop repeat 5 do (format t "~A~%" (counter)))
;;;-----------------------------
(setf (symbol-function 'counter1) (counter-maker)
      (symbol-function 'counter2) (counter-maker))

(loop repeat 5 do (format t "~A~%" (counter1)))
(format t "~A ~A~%" (counter1) (counter2))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5 0
;;;-----------------------------
(defun timestamp ()
  (let ((start-time (get-universal-time)))
    #'(lambda ()
        (- (get-universal-time) start-time))))

(setf (symbol-function 'early) (timestamp))
(sleep 20)
(setf (symbol-function 'later) (timestamp))
(sleep 10)
(format t "It's been ~D seconds since early.~%" (early))
(format t "It's been ~D seconds since later.~%" (later))
;; It's been 30 seconds since early.
;; It's been 10 seconds since later.
;;;-----------------------------

;;; @@PLEAC@@_11.5
;;;-----------------------------
;; Although you can't get a reference directly to the scalar that a
;; symbol points to (at least, not if it's a number or character), you
;; can just refer to the symbol itself, for largely the same effect.
(setf scalar-ref 'scalar)               ; get reference to symbol
;;;-----------------------------
;; Not sure what this was trying to demonstrate.
;;undef $anon_scalar_ref;
;;$$anon_scalar_ref = 15;
;;;-----------------------------
;; There's no way to do this in CL that I know of.
;;$anon_scalar_ref = \15;
;;;-----------------------------
(format t "~A" (symbol-value scalar-ref)) ; dereference it
(setf (symbol-value scalar-ref) (concatenate 'string (symbol-value scalar-ref) "string"))
;;;-----------------------------
;; This is a BAD idea in CL.  Symbols are relatively expensive, there
;; is no guarantee that name collisions won't happen, possible memory
;; leak issues, etc.
(let ((symbol-number -1))
  (defun new-anon-symbol ()
    (intern (format nil "_NEWANONSYM~D" (incf symbol-number)))))
;;;-----------------------------
(setf sref (new-anon-symbol)
      (symbol-value sref) 3)
(format t "Three = ~A~%" (symbol-value sref))
(setf my-array (vector (new-anon-symbol) (new-anon-symbol)))
(setf (symbol-value (svref my-array 0)) 6.02e23
      (symbol-value (svref my-array 1)) "avocado")
(format t "ARRAY contains: ~{~A~^, ~}~%" (map 'list 'symbol-value array))
;;;-----------------------------
(setf var (with-output-to-string (output)
            (sb-ext:run-program "uptime" nil :search t :output output)))
(setf vref 'var)
(when (scan "load" (symbol-value vref)))
(setf (symbol-value vref) (chomp (symbol-value vref)))
;;;-----------------------------
;; check whether SOMEREF contains a reference to a symbol, which we're
;; using instead of Perl's scalar references.
(check-type someref 'symbol)            ; does the die() for us
;;;-----------------------------

;;; @@PLEAC@@_11.6
;;;-----------------------------
(setf array-of-scalar-refs (vector 'a 'b))
;;;-----------------------------
;; Note that because #() quotes its contents, A and B refer to the
;; symbols A and B, not their values, which is the closest
;; approximation to what the Perl is doing.
(setf array-of-scalar-refs #(a b))
;;;-----------------------------
(setf (symbol-value (aref array-of-scalar-refs 1)) 12) ; B = 12
;;;-----------------------------
(setq a 1 b 2 c 3 d 4)                  ; initialize
(setf my-array (vector 'a 'b 'c 'd))    ; refs to each symbol
(setf my-array #(a b c d))              ; same thing!
(setf my-array (loop repeat 4 collect (new-anon-symbol))) ; allocate 4 anon symbols

(incf (symbol-value (aref my-array 2)) 9) ; C now 12

(symbol-macrolet ((element (symbol-value (aref my-array (1- (length my-array))))))
  (setf element (* element 5))          ; D now 20
  (setf element (* element 5)))         ; D now 100

(let ((tmp (aref my-array (1- (length my-array)))))   ; using temporary
  (setf (symbol-value tmp) (* 5 (symbol-value tmp)))) ; D now 500
;;;-----------------------------
;; Note that PI is built in to CL.
(map 'nil 
     #'(lambda (sref)
         "Replace with spherical volumes."
         (symbol-macrolet ((element (symbol-value sref)))
           (setf element (* (expt element 3)
                            (* 4/3 pi)))))
     my-array)
;;;-----------------------------

;;; @@PLEAC@@_11.7
;;;-----------------------------
(setf c1 (mkcounter 20)
      c2 (mkcounter 77))

(format t "next c1: ~d~%" (funcall (gethash "NEXT" c1))) ; 21 
(format t "next c2: ~d~%" (funcall (gethash "NEXT" c2))) ; 78 
(format t "next c1: ~d~%" (funcall (gethash "NEXT" c1))) ; 22 
(format t "last c1: ~d~%" (funcall (gethash "PREV" c1))) ; 21 
(format t "old  c2: ~d~%" (funcall (gethash "RESET" c2))) ; 77
;;;-----------------------------
(defun mkcounter (start)
  (let* ((count start)
         (bundle
          ;; MKHASH defined in appendix
          (mkhash 
           "NEXT"   #'(lambda () (incf count))
           "PREV"   #'(lambda () (decf count))
           "GET"    #'(lambda () count)
           "SET"    #'(lambda (new-count) (setf count new-count))
           "BUMP"   #'(lambda (delta) (incf count delta))
           "RESET"  #'(lambda () (setf count start)))))
    (setf (gethash "LAST" bundle) (gethash "PREV" bundle))
    bundle))
;;;-----------------------------

;;; @@PLEAC@@_11.8
;;;-----------------------------
;; Methods in CL are generic functions that can be specialized on any
;; of their arguments.  The technique that the Perl code is using to
;; allow calling meth() without the $obj-> is needless in CL.  You can
;; make a reference to a method, the same way you might make a
;; referece a normal function.  There's not much point in doing this,
;; normally.
(setf (symbol-function 'mref) #'meth)
;; later...
(mref "args" "go" "here")
;;;-----------------------------
(setf (symbol-funtion 'sref) #'meth)
;;;-----------------------------

;;; @@PLEAC@@_11.9
;;;-----------------------------
;; MKHASH defined in appendix
(setf record (mkhash
              :name    "Jason"
              :empno   132
              :title   "deputy peon"
              :age     23
              :salary  37000
              :pals    '("Norbert" "Rhys" "Phineas")))

(format t "I am ~A, and my pals are ~{~A~^, ~}~%"
        (gethash :name record)
        (gethash :pals record))
;;;-----------------------------
(defparameter *byname* (make-hash-table :test 'equal))
;; store record
(setf (gethash (gethash :name record) *byname*) record)

;; later on, look up by name
(let ((rp (gethash "Aron" *byname*)))
  (when rp
    (format t "Aron is employee number ~D~%" (gethash :empno rp))))

;; give jason a new pal
(push "Theodore" (gethash :pals (gethash "Jason" *byname*)))
(format t "Jason now has ~D pals~%" (length (gethash :pals (gethash "Jason" *byname*))))
;;;-----------------------------
;; Go through all records
(maphash #'(lambda (name record)
             (format t "~A is employee number ~D~%" name (gethash :empno record)))
         *byname*)
;;;-----------------------------
;; store record
(defparameter *employees* (make-array 0 :adjustable t))
(let ((empno (gethash :empno record)))
  (unless (array-in-bounds-p *employees* empno)
    ;; :INITIAL-ELEMENT used to prevent array from being extended with 0's
    (adjust-array *employees* (1+ empno) :initial-element nil))
  (setf (aref *employees* empno) record))

;; lookup by id
(when-let (rp (aref *employees* 132))
  (format t "employee 132 is ~A~%" (gethash :name rp)))
;;;-----------------------------
;; The use of SYMBOL-MACROLET saves us from repeating a complicated
;; expression multiple times.  Could also define a macro to make this
;; easier.
(symbol-macrolet ((salary (gethash :salary (gethash "Jason" *byname*))))
  (setf salary (* salary 1.035)))
;;;-----------------------------
(setf peons (remove-if-not
             #'(lambda (employee)
                 (and employee
                      (scan (create-scanner "peon" :case-insensitive-mode t)
                                     (gethash :title employee))))
             *employees*))
;; Or:
(setf peons (perl-grep *employees*
              (and it
                   (scan (create-scanner "peon" :case-insensitive-mode t)
                                  (gethash :title it)))))

(setf tsevens (remove-if-not 
               #'(lambda (employee) 
                   (and employee
                        (= (gethash :age employee) 
                           27)))
               *employees*))
;;;-----------------------------
(defun hash-slice (hash &rest keys)
  "Meant to emulate Perl's built-in hash slicing feature."
  (loop for key in keys collect (gethash key hash)))

(dolist (rp (sort (loop for v being the hash-values of *byname* collect v)
                 'string-lessp
                 :key #'(lambda (hash) (gethash :age hash))))
  (format t "~A is employee number ~D.~%" (gethash :name rp) (gethash :age rp))
  ;; or with a hash slice on the reference (note that we're using the
  ;; non-standard HASH-SLICE function defined above, whereas Perl
  ;; has a built-in to do it).
  (apply 'format t "~A is employee number ~D.~%" (hash-slice rp :name :age)))
;;;-----------------------------
;; use BYAGE, an array of lists of records
(push record (aref *byage* (gethash :age record)))
;;;-----------------------------
(dotimes (age (length *byage*))
  (when-let (records (aref *byage* age))
    (format t "Age ~D: " age)
    (dolist (rp records)
      (format t "~A " (gethash :name rp)))
    (format t "~%")))
;;;-----------------------------
(dotimes (age (length *byage*))
  (when-let (records (aref *byage* age))
    (format t "Age ~D: ~{~A~^, ~}~%" age (map 'list #'(lambda (employee)
                                                        (gethash :name employee))
                                              (aref *byage* age)))))
;;;-----------------------------

;;; @@PLEAC@@_11.10
;;;-----------------------------
;; FieldName: Value
;;;-----------------------------
;; Using list instead of array since random access not required.
(defparameter *list-of-records* nil)

(map nil #'(lambda (record)
             (dolist (key (sort (loop for k being the hash-keys of record collect k)
                                'string-lessp :key 'symbol-name))
               (format t "~A: ~A~%" key (gethash key record)))
             (terpri))                  ; same as (format t "~%")
     *list-of-records*)
;;;-----------------------------
;; GET-PARAGRAPH defined above
(dolist (filename filenames)
  (with-open-file (file filename)
    (loop
       for paragraph = (get-paragraph file)
       until (string-equal paragraph "")
       do
       (let ((fields
              (split (create-scanner "^([^:]+):\\s*" :multi-line-mode t)
                              paragraph :with-registers-p t)))
         (push (apply 'mkhash (cdr fields)) *list-of-records*)))))
;;;-----------------------------

;;; @@PLEAC@@_11.15
;;;-----------------------------

;;; Binary trees example
(deftype tree () '(or null tree-node))
(defstruct (tree-node
             (:conc-name #:tree-))
  value
  ;; subtrees
  (left nil :type tree)
  (right nil :type tree))

(defun tree-insert (tree value)
  "Return TREE with destructively inserted VALUE."
  (declare (type tree tree))
  (if tree
      (progn (if (< value (tree-value tree))
                 (setf (tree-left tree)
                       (tree-insert (tree-left tree) value))
                 (setf (tree-right tree)
                       (tree-insert (tree-right tree) value)))
             tree)
      (make-tree-node :value value)))

(defun print-tree-in-order (tree)
  (when tree
    (print-tree-in-order (tree-left tree))
    (format t "~S " (tree-value tree))
    (print-tree-in-order (tree-right tree))))

(defun print-tree-in-preorder (tree)
  (when tree
    (format t "~S " (tree-value tree))
    (print-tree-in-preorder (tree-left tree))
    (print-tree-in-preorder (tree-right tree))))

(defun print-tree-in-postorder (tree)
  (when tree
    (print-tree-in-postorder (tree-left tree))
    (print-tree-in-postorder (tree-right tree))
    (format t "~S " (tree-value tree))))

(defun search-tree (tree value)
  "Return a subtree of TREE with the specified VALUE in root."
  (when tree
    (if (= (tree-value tree) value)
        tree
        (search-tree (if (< value (tree-value tree))
                         (tree-left tree)
                         (tree-right tree))
                     value))))

(defun test-trees ()
  (let ((tree nil))
    (dotimes (i 20)
      (setf tree (tree-insert tree (random 1000))))
    (format t "~&Pre order: ") (print-tree-in-preorder tree)
    (format t "~&In order:  ") (print-tree-in-order tree)
    (format t "~&Postorder: ") (print-tree-in-postorder tree)
    (terpri)

    (loop do
         (format t "~&Search? ")
         (finish-output)
         (let* ((eof (gensym)) ; some hard-to-enter object
                (value (read *standard-input* nil eof)))
           (when (eq value eof) (loop-finish))
           (let ((found (search-tree tree value)))
             (if found
                 (format t "Found ~S at ~S~%" value found)
                 (format t "No ~S in tree~%" value)))))))
;;;-----------------------------

;;; @@PLEAC@@_12.0
;;;-----------------------------
(defpackage :alpha (:use :cl))
(in-package :alpha)
(setf name "first")

(defpackage :omega (:use :cl))
(in-package :omega)
(setf name "last")

(in-package :cl-user) ; default package, kinda like Perl's 'main' package
(format t "Alpha is ~A, omega is ~A.~%" alpha::name omega::name)
;; Alpha is first, omega is last.
;;;-----------------------------
(load "FileHandle.lisp")                ; run-time load
(load "FileHandle.fasl")        ; explicitly load pre-compiled version
(load "FileHandle")          ; same as previous two, will prefer .fasl
(require :FileHandle)                   ; similar, still run-time

;; Could use LOAD here instead of REQUIRE; it's the EVAL-WHEN that
;; makes this compile-time.  Note that it's fairly unusual to do this
;; explicitly with EVAL-WHEN.  Instead, one normally ensures that the
;; requisite packages have already been loaded before attempting to
;; compile a file, e.g., using a build system such as ASDF.
(eval-when (:compile-toplevel :load-toplevel :execute)
  (require :FileHandle))                ; compile-time

;; The Cards::Poker example is no different than the above in CL.  CL
;; has no in-built hierarchicalp package system, hence no translation
;; of "::" into a directory separator, etc.
;;;-----------------------------
(defpackage :cards.poker                
  (:use :cl)                            ; equivalent to Perl line 2
  (:export :shuffle :*card-deck*))      ; line 4
(in-package :cards.poker)               ; line 1
(defparameter *card-deck* nil)          ; line 5
(defun shuffle ())                      ; line 6
;;;-----------------------------

;;; @@PLEAC@@_12.1
;;;-----------------------------
(defpackage :your-module (:use :cl)
            (:export ...))              ; one way to export
(in-package :your-module)

(defparameter *version* 1.00) ; not standard CL, but one could ascribe meaning by convention

(export '(...))                         ; another way to export

;; There is no built-in equivalent to EXPORT_OK.  EXPORT_TAGS also has
;; no standard CL counterpart, but something similar can easily be
;; implemented.  See IMPORT-TAGS, defined in the appendix.
(defparameter *export-tags*
  '(:TAG1 ( ... )
    :TAG2 ( ... )))

;;;;;;;;;;;;;;;;;;;;;;;;
;; your code goes here
;;;;;;;;;;;;;;;;;;;;;;;;
;;;-----------------------------
(defpackage :my-package (:use cl)
            (:use :your-module)) ; Import default symbols into my package.
(defpackage :my-package (:use cl)
            (:import-from :your-module ...)) ; Import listed symbols into my package.
;; Or at some point in my-package do this:
(in-package :my-package)
(import '(your-module:symbol1 your-module:symbol2 ...)) ; Import listed symbols into my package.
(defpackage :my-package (:use cl)
            (:import-from :your-module)) ; Do not import any symbols
;;;-----------------------------
(export '(f1 f2 my-list))
;;;-----------------------------
;; No equivalent to EXPORT_OK
;;;-----------------------------
(import '(your-module:op-func your-module:your-table your-module:f1))
;;;-----------------------------
;; From within the package you want to import into, do this
;; (IMPORT-TAGS defined in appendix, not standard CL).
(import-tags :your-module :DEFAULT)
(import 'your-module:your-table)
;;;-----------------------------
(defparameter *export-tags*
  '((:functions (f1 f2 op-func))
    (:variables (your-list your-table))))
;;;-----------------------------
(import-tags :your-module :functions)
(import 'your-module:your-table)
;;;-----------------------------

;;; @@PLEAC@@_12.2
;;;-----------------------------
;; In the following example, EVAL-WHEN is like the Perl example's
;; BEGIN.
(eval-when (:compile-toplevel :load-toplevel :execute)
  (multiple-value-bind (retval why-not)
      (ignore-errors (require :mod))    ; no import
    (when why-not
      (warn "couldn't load :mod: ~A" why-not))))

;; Note that there is no "use" in CL, but you can REQUIRE and then
;; USE-PACKAGE if it was successful.
(eval-when (:compile-toplevel :load-toplevel :execute)
  (multiple-value-bind (retval why-not)
      (ignore-errors (require :mod))
    (if why-not
      (warn "couldn't load :mod: ~A" why-not)
      (use-package :mod))))             ; imports into current package
;;;-----------------------------

;; This code is written in order to be as much like the Perl example
;; as possible, and likely isn't what you'd use in practice.  Normally
;; you'd just take advantage of the existing features of, for example,
;; ASDF.
(eval-when (:compile-toplevel :load-toplevel :execute)

  (let ((dbs '(giant.eenie giant.meanie mouse.mynie moe))
        found)

    (dolist (module dbs)
      (multiple-value-bind (_ why-not)
          (ignore-errors (require module))
        (unless why-not
          (let (import-fn)
            (let ((*package* (find-package module)))
              ;; The following line assumes the module defines
              ;; MY-IMPORT (note it can't be called IMPORT b/c that
              ;; clashes with CL's built-in IMPORT).
              (setf import-fn (symbol-function (find-symbol "MY-IMPORT")))
            (funcall import-fn)))
          (setf found t)
          (return))))

    (unless found (error "None of ~{~A~^ ~} loaded" dbs))))
;;;-----------------------------

;;; @@PLEAC@@_12.3
;;;-----------------------------
(eval-when (:compile-toplevel :load-toplevel :execute)
  (unless (and (= 3 (length *posix-argv*)) ; Perl skips past executable
               (= 2 (length (perl-grep *posix-argv* ; PERL-GREP defined in Appendix
                              (scan "^\\d+$" it)))))
    (error "usage: ~A num1 num2" (car *posix-argv*))))

(require :some.module)
(require :more.modules)
;;;-----------------------------
;; This would be silly since CL's numbers already are "big".
(when opt-b (require :math.bigint)) 
;;;-----------------------------
;; The following is just to make the examples work.
(defpackage :fcntl (:use cl))
(in-package :fcntl)
(defconstant +O-EXCL+ #x800)
(defconstant +O-CREAT+ #x200)
(defconstant +O-RDWR+ #x2)

(export '(+O-EXCL+ +O-CREAT+ +O-RDWR+))
(provide 'fcntl)

(defpackage :my-package
  (:use cl)
  (:import-from :fcntl +O-EXCL+ +O-CREAT+ +O-RDWR+))
(in-package :my-package)
;;;-----------------------------
(require :fcntl)
(import '(fcntl:+O-EXCL+ fcntl:+O-CREAT+ fcntl:+O-RDWR+))
;;;-----------------------------
(in-package cl-user)
(defun load-module (module)
  (require module)
  (import module))                      ; WRONG
;;;-----------------------------
(load-module :fcntl '(fcntl:+O-EXCL+ fcntl:+O-CREAT+ fcntl:+O-RDWR+))
(defun load-module (module symbols)
  (require module)
  ;; No need for die because REQUIRE will do it for us.
  (import symbols))
;;;-----------------------------
;; CL doesn't have anything like Perl's autouse, as far as I can tell.
;; The following is a rough implementation of it.  Usage:
;;   (autouse 'my-package (xyz abc))
;; Where XYZ and ABC are functions defined within MY-PACKAGE.
(defmacro autouse (pack symbols)
  (list*
   'progn
   (loop
      for symb in symbols
      collect
        `(defun ,symb (&rest args)
           (require ',pack)
           (apply (find-symbol ,(symbol-name symb) ',pack) args)))))
;;;-----------------------------

;;; @@PLEAC@@_12.4
;;;-----------------------------
(defpackage :alpha (:use :cl))
(in-package :alpha)

(defparameter aa 10)
(export 'aa)
(defparameter x "azure")

(defpackage :beta (:use :cl))
(in-package :beta)

(defparameter bb 20)
(export 'bb)
(defparameter x "blue")

(in-package :cl-user)         ; closest thing to Perl's 'main' package
;; Unlike the Perl example, without the explict EXPORT and IMPORT the
;; symbols won't be visible in the CL-USER package.
(import '(alpha:aa beta:bb))
(format t "~A, ~A, ~A, ~A, ~A~%" aa bb (if (boundp 'x) x "") alpha::x beta::x)
;; 10, 20, , azure, blue
;;;-----------------------------
;; flipper.lisp
(defpackage :flipper
  (:use cl cl-ppcre)
  (:export flip-words flip-boundary))
(in-package :flipper)

(defvar *separatrix* #\Space) ; default to blank; must precede functions

(defun flip-boundary (&optional separatrix)
  (prog1 *separatrix*
    (when separatrix
      (setf *separatrix* separatrix))))

(defun flip-words (line)
  (let ((words (split *separatrix* line)))
    (format nil (format nil "~~{~~A~~^~A~~}" *separatrix*) (reverse words))))
;;;-----------------------------
    
;;; @@PLEAC@@_12.5
;;;-----------------------------
;; #. forces *PACKAGE* to be evaluated at compile-time, so that it
;; won't pick up the dynamic value of *PACKAGE* (i.e., the package of
;; the calling function, which may be different.)
(setf this-pack #.*package*)
;;;-----------------------------
;; As long as the following does not appear at the "top level" of the
;; file, it will contain the current package (i.e., the "outermost"
;; calling function's, assuming it hasn't been rebound explicitly.)
(setf that-pack *package*)
;;;-----------------------------
(format t "I am in package *package*~%")        ; WRONG!
;; I am in package *package*
;;;-----------------------------
(defpackage :alpha (:use cl beta))
(in-package :alpha)

;; As (I think) the Perl example does, this presupposes that TEMP is
;; set to an already-open stream (in BETA), e.g.:
;;  (setf temp (open "/usr/share/dict/words"))
(runit "(setf line (read-line temp))")

(defpackage :beta 
  (:use cl)
  (:export runit))
(in-package :beta)

(defun runit (codestr)
  ;; The following is not a good idea, but is intended to make this
  ;; example work the way the Perl one does, by causing the EVAL to
  ;; occur in the context of this package (BETA) rather than the
  ;; caller's (ALPHA's).
  (in-package #.(package-name *package*))
  ;; EVAL will throw an error if there's any problems, no need to do
  ;; it explicitly like the Perl does.
  (eval (read-from-string codestr)))
;;;-----------------------------
(defpackage :beta 
  (:use cl)
  (:export runit))
(in-package :beta)

(defun runit (codestr)
  ;; CL is essentially the reverse of Perl in this regard, the default
  ;; behavior already works the right way without the need to use
  ;; something like Perl's 'caller'.
  (eval (read-from-string codestr)))
;;;-----------------------------
(defpackage :alpha (:use cl beta))
(in-package :alpha)

(runit (lambda () (setf line (read-line temp))))

(defpackage :beta
  (:use cl)
  (:export runit))
(in-package :beta)

(defun runit (coderef)
  (funcall coderef))
;;;-----------------------------
(in-package cl-user)

(defparameter *fh* (open "/etc/services")) ; don't have /etc/termcap on my machine
(multiple-value-setq (a b c) (values-list (nreadline 3 "*fh*")))

(defun nreadline (count handle)
  (unless (plusp count) (error "COUNT must be > 0"))
  (let ((handle (symbol-value (find-symbol (string-upcase handle)))))
    (unless (open-stream-p handle)
      (error "need open filehandle"))
    (loop
       repeat count
       collect (read-line handle))))
;;;-----------------------------

;;; @@PLEAC@@_12.7
;;;-----------------------------
;; In section 12.7 we assume the use of ASDF, which is the de facto
;; standard package mechanism.
(loop 
   for path in asdf:*central-registry*
   for i from 0
   do (format t "~D ~A~%" i path))
;;0 /Users/mongo/moogle-code/cl-fnord/
;;1 /Users/mongo/common-lisp.net/cl-zztop/trunk/
;;2 (MERGE-PATHNAMES .sbcl/systems/ (USER-HOMEDIR-PATHNAME))
;;3 (LET ((HOME (POSIX-GETENV SBCL_HOME)))
;;    (WHEN HOME (MERGE-PATHNAMES site-systems/ (TRUENAME HOME))))
;;4 *DEFAULT-PATHNAME-DEFAULTS*

;;;-----------------------------
(pushnew "/projects/spectre/lib" asdf:*central-registry*)
;;;-----------------------------
(require :find-bin)
(pushnew find-bin::*bin* asdf:*central-registry*)
;;;-----------------------------
(shadowing-import find-bin::*bin*)
(pushnew (concatenate 'string *bin* "/../lib"))
;;;-----------------------------

;;; @@PLEAC@@_13.0
;;;-----------------------------
;; In CL you don't "bless" hash tables (or other data structures), as
;; classes, you just create instances which manage their own storage.
;; See section 13.1 for an example of how to create a more Perl-like
;; object, if you want to.
(setf obj (make-instance 'data::encoder))
;;;-----------------------------
(let ((obj #(3 5)))
  (format t "~A ~A~%" (type-of obj) (aref obj 1))
  (setf obj (make-instance 'human::cannibal))
  ;; This part isn't exactly like the Perl, there's no way to reuse
  ;; the array as the underlying storage for the class (as far as I
  ;; know).
  (format t "~A~%" (type-of obj)))
;; (SIMPLE-VECTOR 2) 5
;; CANNIBAL
;;;-----------------------------
(setf (slot-value obj 'stomach) "Empty" ; directly accessing an object's contents
      (slot-value obj 'NAME) "Thag") ; uppercase field name to make it stand out (optional)
;;;-----------------------------
;; The following won't run (due to data::encoder being fake), it just
;; illustrates the syntax of calling a method.
(setf encoded (encode obj "data"))
;;;-----------------------------
;; Not really different from the above, but more in the spirit of the
;; Perl example.
(setf encoded (data::encode obj "data"))
;;;-----------------------------
;; This is already built in to CL, MAKE-INSTANCE, no need to define it
;; ourselves.
;;;-----------------------------
(setf object (make-instance 'my-class))
;;;-----------------------------
;; No difference from above
;; $object = Class::new("Class");
;;;-----------------------------
;; Note: haven't checked whether the following is the best way to do
;; the following.  It does seem to work tho.

;; The following will only get called if the type is a class.
(defmethod class-only-method ((class standard-class))
  ;; more code here
  )
;; Or you could just write a function
(defun class-only-method (class)
  (check-type class standard-class)
  ;; more code here
  )

;; For more specific classes
(defmethod my-class-only-method ((class (eql (find-class 'my-class))))
  ;; more code here
  )
;; Or you could just write a function
(defun my-class-only-method (class)
  (check-type class #.(find-class 'my-class))
  ;; more code here
  )
;;;-----------------------------
(defparameter *lector* (make-instance 'human::cannibal))
(feed *lector* "Zak")
(move *lector* "New York")
;;;-----------------------------
;; No difference from previous subsection.
;;;-----------------------------
(format *error-output* "stuff here~%")
;;;-----------------------------
;; Not sure what the Perl was trying to show here
(move (slot-value obj 'field))
(move (aref ary i))
;;;-----------------------------
(slot-value (move obj) 'field)
(aref (move ary) i)           ; won't work if ARY is actually an array
;;;-----------------------------
;; Not sure what the Perl code was trying to show
;;;-----------------------------

;;; @@PLEAC@@_13.1
;;;-----------------------------
;; This entire subsection is specific to Perl's object system (or lack
;; thereof), so it's difficult to write analogous CL code for it.
;; Most of the examples would be done in CL using MAKE-INSTANCE (or a
;; custom function wrapping MAKE-INSTANCE).

;; For the example of an initialization routine, here is one standard
;; way to do it.
(defmethod initialize-instance :after ((obj my-class) &rest init-args)
  "Initialize an object of MY-CLASS"
  (setf (slot-value obj 'SOMETHING) 'mumble
        (slot-value obj 'SOMETHING-ELSE) 'bumble)
  ;; ... etc
  )

;; On the other hand, it wouldn't be sporting not to at least try to
;; mimic the Perl examples, so here goes.  

;; The following will be used in subsequent examples that attempt to
;; be like Perl.
(defclass perl-object () 
  ((_hash :initform (make-hash-table)
          :type hash-table))
  (:documentation "Can be used as a mixin class to make your class
  more Perl-like, or can be used directly."))

(defmethod slot-missing (class (instance perl-object) slot-name operation 
                         &optional (new-value nil new-value-supplied-p))
  (symbol-macrolet ((hash-place (gethash slot-name (slot-value instance '_hash))))
    (if new-value-supplied-p
        (setf hash-place new-value)
        hash-place)))

;; Here's an example of how to use PERL-OBJECT as a "mixin" class to
;; make your class Perl-like (i.e., able to store arbitrary slots in
;; an underlying hash).
(defclass my-perlish-class (perl-object)
  (a b c))

;; Simliar to the new() function in the Perl.
(defun perl-new (class)
  (assert (subtypep class 'perl-object))
  (make-instance class))
;;;-----------------------------
;; No difference
;;;-----------------------------
;; No difference
;;;-----------------------------
(defun new ()
  (let ((self (make-instance 'perl-object))) ; allocate perl-object w/ anonymous hash
    ;; init two sample attributes/data members/fields
    (setf (slot-value self 'START) (get-universal-time)
          (slot-value self 'AGE) 0)
    self))
;;;-----------------------------
(defun new (classname)
  (assert (subtypep classname 'perl-object)) ; Make sure it is of the right type
  (let ((self (make-instance classname))) ; Allocate new memory
    ;; init data fields
    (setf (slot-value self 'START) (get-universal-time)
          (slot-value self 'AGE) 0)
    self))                              ; And give it back
;;;-----------------------------
(defun new (classname &rest initargs)
  (assert (subtypep classname 'perl-object)) ; Make sure it is of the right type
  (let ((self (make-instance classname))) ; Allocate new memory
    ;; init data fields
    (apply '_init self initargs)
    self))                              ; And give it back

;; "private" method to initialize fields.  It always sets START to the
;; current time, and AGE to 0.  If called with arguments, _init
;; interprets them as key+value pairs to initialize the object with.
;; Note that you have to define this for each individual class you
;; want to have it called upon.  In the example below I used
;; MY-PERLISH-CLASS, you'd obviously have to change that to whatever
;; your class was called.
(defmethod _init ((self my-perlish-class) &rest args)
  (setf (slot-value self 'start) (get-universal-time)
        (slot-value self 'age) 0)
  (loop
     for key in args by #'cddr 
     for value in (cdr args) by #'cddr
     do
       (setf (slot-value self key) value)))
;;;-----------------------------

;;; @@PLEAC@@_13.2
;;;-----------------------------

;; CL has no equivalent to Perl's DESTROY.  Classes that need similar
;; functionality should provide a WITH- macro to handle automatically
;; freeing up the resources in exceptional circumstances.  Here's a
;; skeleton implementation of a macro that one might provide with
;; one's class.
(defmacro with-my-class ((my-obj &rest initargs) &body body)
  `(let ((,my-obj (make-instance 'my-class ,@initargs)))
     (unwind-protect
          (progn ,@body)
       (close ,my-obj)))) ; CLOSE is just an example, should be
                          ; whatever is necessary

;; Even if a condition is signaled, the cleanup code will be called, as in:
;;
;; (with-my-class (obj foo bar) 
;;    (/ 3 0))

;; If you're willing to use a non-standard extension, you can use,
;; e.g., SBCL's SB-EXT:FINALIZE
;;; @@INCOMPLETE@@
;;;-----------------------------

;;; @@PLEAC@@_13.3
;;;-----------------------------
;; Don't do the following, it's just here to match the Perl snippet.
(defun get-name (self)
  (slot-value self 'name))

(defun set-name (self value)
  (setf (slot-value self 'name) value))
;;;-----------------------------
;; The following is the recommended way to do what the Perl example is
;; doing and is normally preferred to the previous example.  It
;; creates a method called NAME which does, essentially, the same
;; thing as the name() function in the example.
(defclass my-class ()
  ((name :accessor name)))
(setf (name my-obj) 'foo                ; just an example
      val (name my-obj))                ; just an example

;; Just for the record, you could also do this (don't actually do it,
;; this is just to illustrate :READER and :WRITER).
(defclass my-class ()
  ((name :reader get-name :writer set-name)))
(set-name 'foo my-obj)                      ; just an example
(get-name my-obj)                           ; just an example

;; You could also do the same thing as the previous example manually.
(defclass my-class ()
  (name))

(defmethod get-name ((my-obj my-class))
  (slot-value my-obj 'name))

(defmethod set-name ((my-obj my-class) value)
  (setf (slot-value my-obj 'name) value))
;;;-----------------------------
(defun age (obj &optional (value nil value-supplied-p))
  (prog1 (slot-value obj 'age)
    (when value-supplied-p
      (setf (slot-value obj 'age) value))))
;; sample call of get and set: happy birthday!
(age obj (1+ (age obj)))
;;;-----------------------------
(defclass person () 
  ((name :accessor name)
   (age :accessor age)
   (peers :accessor peers)))
(defparameter *him* (make-instance 'person))
(setf (slot-value *him* 'name) "Sylvester"
      (slot-value *him* 'age) 23)
;;;-----------------------------
;; Naming this different from Perl snippet, to avoid clashing with
;; NAME accessor above.
(defun person-name (self &optional (value nil value-supplied-p))
  (if value-supplied-p
      (progn
        ;; CL doesn't have an equivalent of -w (which turns on $^W,
        ;; AFAICT), so we always warn.
        (and (scan "[^\\s\\w'-]" value) (warn "funny characters in name"))
        (and (scan "\\d" value) (warn "numbers in name"))
        (or (scan "\\S+(\\s+\\S+)+" value) (warn "prefer multiword name"))
        (or (scan "\\S" value) (warn "name is blank"))
        (setf (name self) (string-upcase value)))
      (name self)))
;;;-----------------------------
;; Most of this subsection (NAME, AGE, PEERS) is already implemented
;; by the DEFCLASS above.
(defmethod exclaim ((self person))
  (with-accessors ((name name) (age age) (peers peers)) self
    (format nil
            "Hi, I'm ~A, age ~D, working with ~{~A~^, ~}"
            name age peers)))

(defmethod happy-birthday ((self person))
  (incf (age self)))
;;;-----------------------------


;;; @@PLEAC@@_13.4
;;;-----------------------------
(defclass person ()
  ((gender :accessor gender :initarg :gender)))

(let ((body-count 0))
  (defun population ()
    body-count)
  
  (defmethod initialize-instance :after ((self person) &rest initargs)
    (declare (ignore initargs))
    (incf body-count))
  
  ;; Note that using standard CL you would have to arrange for this to
  ;; be called somehow.  It won't be called automatically by the
  ;; garbage collector, as it would in Perl.
  (defmethod destroy ((self person))
    (declare (ignore self))
    (decf body-count))
  )

(defvar *people* nil)
(loop repeat 10 do (push (make-instance 'person) *people*))
(format t "There are ~D people alive.~%" (population))
;; There are 10 people alive.
;;;-----------------------------
(defparameter *him* (make-instance 'person :gender :male))
(defparameter *her* (make-instance 'person :gender :female))
;;;-----------------------------
(fixed-array-max-bounds 100)            ; set for whole class
(defparameter *alpha* (make-instance 'fixed-array))
(format t "Bound on *alpha* is ~D~%" (max-bounds *alpha*))
;; Bound on *alpha* is 100

(defparameter *beta* (make-instance 'fixed-array))
(max-bounds *beta* 50)                  ; still sets for whole class
(format t "Bound on *alpha* is ~D~%" (max-bounds *alpha*))
;; Bound on *alpha* is 50
;;;-----------------------------
(defclass fixed-array () ())

(let ((bounds 7))
  (macrolet ((bounds-body ()
               `(if value-supplied-p
                    (setf bounds value)
                    bounds)))

    (defmethod max-bounds ((fixed-array fixed-array) &optional (value nil value-supplied-p))
      (bounds-body))

    ;; Don't need this, except to be a little more like the Perl code
    (defun fixed-array-max-bounds (&optional (value nil value-supplied-p))
      (bounds-body))
    ))
;;;-----------------------------
;; Already implemented in previous snippet
;;;-----------------------------
;; Don't do this.  To match the Perl code we have to redefine the
;; above so that FIXED-ARRAY takes a reference to BOUNDS.  The easiest
;; way to do that is to make BOUNDS a symbol, there's no direct way to
;; store a reference to a number in CL since a number can be passed
;; around by value.
(defclass fixed-array () (max-bounds-ref))

(let ((bounds-sym (gensym "BOUNDS-"))) ; a symbol, so we can store a ref to int
  (eval-when (:compile-toplevel :load-toplevel :execute)
    (setf (symbol-value bounds-sym) 7))
  (defmethod initialize-instance :after ((self fixed-array) &rest initargs)
    (declare (ignore initargs))
    (setf (slot-value self 'max-bounds-ref) bounds-sym))

  (defmethod max-bounds ((fixed-array fixed-array) &optional (value nil value-supplied-p))
    (if value-supplied-p
        (setf (symbol-value (slot-value fixed-array 'max-bounds-ref)) value)
        (symbol-value (slot-value fixed-array 'max-bounds-ref))))

  (defun fixed-array-max-bounds (&optional (value nil value-supplied-p))
    (if value-supplied-p
        (setf (symbol-value bounds-sym) value)
        (symbol-value bounds-sym))))
;;;-----------------------------

;;; @@PLEAC@@_13.5
;;;-----------------------------
;; The closest thing to this example would be CL's built-in
;; structures, which are simpler than CLOS objects and which by
;; default store their slots in vectors.

(defstruct person
  name                                  
  age
  (peers nil :type list))

(defparameter *p* (make-person))     ; allocate an empty Person struct

(setf (person-name *p*) "Jason Smythe") ; set its name field
(setf (person-age *p*) 13)              ; set its age field
(setf (person-peers *p*) '("Wilbur" "Ralph" "Fred")) ; set its peers field

;; fetch various values, including the zeroth friend
(format t "At age ~D, ~A's first friend is ~A.~%"
        (person-age *p*)
        (person-name *p*)
        (car (person-peers *p*)))
;;;-----------------------------
(defstruct person name age)
(defstruct family
  (head (make-person) :type person)
  address
  (members nil :type list))

(defparameter *folks* (make-family))
(defparameter *dad* (family-head *folks*))
(setf (person-name *dad*) "John")
(setf (person-age *dad*) 34)

(format t "~A's age is ~D~%"
        (person-name (family-head *folks*)) 
        (person-age (family-head *folks*)))
;;;-----------------------------
;; You can use DEFMETHOD on any type, not just CLOS objects.
(defmethod age ((person person) &optional (value nil value-supplied-p))
  (if value-supplied-p
      (progn
        (setf (person-age person)
              (if (stringp value)
                  (progn
                    (when (not (scan "^\\d+" value)) 
                      (warn "age `~A' isn't numeric" value))
                    (let ((age (parse-integer value :junk-allowed t)))
                      (when (> age 150)
                        (warn "age `~D' is unreasonable" age))
                      age))
                  value)))
      (person-age person)))
;;;-----------------------------
;; No equivalent to $^W
;;;-----------------------------
;; There's still no equivalent to $^W, but the following illustrates
;; how you could do the rest of what the Perl does.
(let ((gripe (if *should-warn* #'warn #'error)))
  (when (not (scan "^\\d+" value)) 
    (funcall gripe "age `~A' isn't numeric" value))
  (let ((age (parse-integer value :junk-allowed t)))
                      (when (> age 150)
                        (funcall gripe "age `~D' is unreasonable" age))
                      age))
;;;-----------------------------
;; Just use DEFSTRUCT as you did above.
;;;-----------------------------
(defstruct card name color cost type release text)
;;;-----------------------------
;; Don't do this.  Very little point, just defining this macro so that
;; the Perl example can be matched.
(defmacro defstruct* (name slots)
  `(defstruct ,name ,@slots))

(defstruct* card #.(mapcar #'identity '(name color cost type release text)))
;;;-----------------------------
(defstruct* hostent #.(mapcar #'identity '(name 
                                           (aliases nil :type list)
                                           addrtype
                                           length
                                           (addr-list nil :type list))))
;;;-----------------------------
;; What is this supposed to mean?
;;#define h_type h_addrtype
;;#define h_addr h_addr_list[0]
;;;-----------------------------
;; make (hostent-addr hostent-object) same as (hostent-addr-list hostent-object)

;; The following has to be a macro so that all the SETF-related stuff
;; will work correctly.
(defmacro hostent-addr (hostent-object)
  `(hostent-addr-list ,hostent-object))
;;;-----------------------------
;; Not sure what the corresponding Perl snippet was trying to show.
;;;-----------------------------

;;; @@PLEAC@@_13.6
;;;-----------------------------
;; Skipping this snippet due to Perl-specificity
;;;-----------------------------
(setf ob1 (make-instance 'some-class))
;; later on
(setf ob2 (make-instance (class-of ob1)))
;;;-----------------------------
;; It's a little unclear what the original Perl is doing.  E.g., I
;; have no idea what PARENT is supposed to be.
(defun new-from-proto (proto)
  ;; We don't need to do all the fancy stuff the Perl code does to get
  ;; the superclass' initializers (equivalent of 'new') to run,
  ;; they'll be run automatically by MAKE-INSTANCE.
  (let ((self (make-instance (class-of proto))))
    ;; The following two lines presume that the class being
    ;; instantiated either has START and AGE accessors defined, or
    ;; else inherits the PERL-OBJECT mixin defined earlier in this
    ;; chapter, so that any accessor will work.
    (setf (slot-value self 'start) (get-universal-time) ; init data fields
          (slot-value self 'age) 0)
    self))
;;;-----------------------------
    
;;; @@PLEAC@@_13.7
;;;-----------------------------
(let ((methname 'flicker)
  (funcall methname obj 10))            ; calls (flicker obj 10) 
    
;; call three methods on the object, by name
(loop for m in '(start run stop)
     do (funcall m obj))
;;;-----------------------------
;; There is no need to do this in CL, in fact in general it won't work
;; because CLOS supports multi-methods, so the method does not
;; "belong" to any one object or class.
;;;-----------------------------
(defparameter *fn-ref* #'(lambda (&rest args) (apply 'my-method args)))
;;;-----------------------------
(funcall *fn-ref* obj 10 "fred")
;;;-----------------------------
(my-method obj 10 "fred")
;;;-----------------------------
;; There is no equivalent of the Perl snippet because methods don't
;; "belong" to a single class.  The following is about as close as you
;; can get but it only works if there is a specific method defined for
;; that class and you know which argument(s) are specialized.
(when (typep (type-of obj) obj-target)
  ;; Find a 3-arg method with the first one specialized to the type of
  ;; OBJ.
  (when (find-method #'my-method '() (list (type-of obj) t t))
    (apply #'my-method obj-target arguments)))
;;;-----------------------------

;;; @@PLEAC@@_13.8
;;;-----------------------------
(typep obj 'http:message)
;; There is no equivalent to Per's can() method, as described earlier
;; methods don't belong to a single class.  You could use FIND-METHOD
;; as the final example in section 13.7 above shows, althouth that's
;; not recommended.
;;;-----------------------------
(setf has-io (typep fd 'io:handle))
(setf itza-handle (typep fd 'io:handle))
;;;-----------------------------
;; No equivalent.  You could use the following after verifying that
;; OBJ was of the right type.
(setf his-print-method #'as-string)
;;;-----------------------------
;; There's no standardized support for versions in CL.
;;;-----------------------------

;;; @@PLEAC@@_13.9
;;;-----------------------------
(defclass person ()
  ((name :accessor name :initarg :name)
   (age :accessor age :initarg :age)))
;;;-----------------------------
(let ((dude (make-instance 'person :name "Jason" :age 23)))
  (format t "~A is age ~D.~%" (name dude) (age dude)))
;;;-----------------------------
(defclass employee (person)
  ())
;;;-----------------------------
(let ((empl (make-instance 'employee :name "Jason" :age 23)))
  (format t "~A is age ~D.~%" (name empl) (age empl)))
;;;-----------------------------
;; No equivalent to this "wrong" Perl snippet.
;;;-----------------------------

;;; @@PLEAC@@_13.10
;;;-----------------------------
(defmethod meth ((self my-class))
  ;; Normally this will result in calling the "superclass" as you
  ;; might think of it in, say, Java.  Note though that CLOS supports
  ;; multiple inheritance, aspect-oriented programming, and various
  ;; other extensions, so CALL-NEXT-METHOD won't *always* do that.
  (call-next-method))
;;;-----------------------------
(meth self)                        ; call wherever first METH is found

;; I can't find any obvious way to do the other examples, you can find
;; a method with FIND-METHOD but there isn't an obviuos way to call it
;; directly on an object.
;;;-----------------------------
;; Don't need to define new(), use MAKE-INSTANCE.

;; Don't need to define _init(), just add an INITIALIZE-INSTANCE
;; method, which will be called automatically and which won't prevent
;; other stuff from running.
(defmethod initialize-instance :after ((self my-class) &rest initargs)
  (with-slots (start age extra)
      (setf (start self) (get-decoded-time) ; init data fields
            (age self)   0
            (extra self) initargs       ; anything extra
;;;-----------------------------
(defparameter *obj* (make-instance 'widget :haircolor :red :freckles 121))
;;;-----------------------------
;; The perl snippet is doing something that's done automatically by
;; CLOS (i.e., all inherited INITIALIZE-INSTANCE methods will be
;; called automatically).
;;;-----------------------------

;;; @@PLEAC@@_13.11
;;;-----------------------------

;; The equivalent of AUTOLOAD is CLOS's SLOT-MISSING functionality,
;; used to define PERL-OBJECT, above.  In order to add the ok_field
;; behavior, we'll subclass it here.
(defclass person (perl-object) 
  ((ok-field :initform (mkhash 'name t 'age t 'peers t 'parent t)
             :type hash-table
             :allocation :class)))

;; This isn't *exactly* like the Perl snippet, e.g., it doesn't do the
;; uppercase checking or checking for DESTROY.  However, since CL
;; isn't case-sensitive and methods are kept separate from slots,
;; there's no need to do either of those things.
;; 
;; This method "intercepts" SLOT-MISSING, which is normally handled by
;; PERL-OBJECT and lets the call through if the slot name is valid.
(defmethod slot-missing (class (instance person) slot-name operation
                         &optional new-value)
  (declare (ignore new-value operation))
  (if (gethash slot-name (slot-value instance 'ok-field))
      (call-next-method)                ; ok, pass control to PERL-OBJECT
      (error "invalid attribute method ~A" slot-name)))
;;;-----------------------------
(let ((dad (make-instance 'person))
      (kid (make-instance 'person)))
  (setf (slot-value dad 'name) "Jason"
        (slot-value dad 'age) 23
        (slot-value kid 'name) "Rachel"
        (slot-value kid 'age) 2
        (slot-value kid 'parent) dad
        )
  (format t "Kid's parent is ~A~%" (slot-value (slot-value kid 'parent) 'name)))
;; Kid's parent is Jason
;;;-----------------------------
;; The above SLOT-MISSING definition already works the same way as
;; this Perl snippet.
;;;-----------------------------

;;; @@PLEAC@@_13.12
;;;-----------------------------
;; With respect to this particular Perl snippet, there's no need to
;; define Employee::age as its equivalent is automatically defined by
;; the DEFCLASS below.
;;;-----------------------------
(defpackage :person (:use cl))
(in-package person)
(defclass person ()
  ((name :accessor name)
   (age :accessor age)
   (peers :accessor peers)
   (parent :accessor parent)))

(export '(person name age peers parent))
;;;-----------------------------
(defpackage :employee (:use cl))
(in-package employee)
(defclass employee (person:person)
  ((salary :accessor salary)
   (age :accessor age)
   (boss :accessor boss)))

(export '(employee salary age boss))
;;;-----------------------------
;; The "data inheritance problem", as far as I can tell, is
;; automatically solved by CL in a similar manner to what the Perl is
;; doing.  Being automatic, there is no need to define a custom class
;; like Class::Attributes.  By putting the different classes into
;; different packages, CL automatically distinguishes the slots by the
;; package name.
;;
;; E.g., the above definitions allow you to write (employee:age obj) or
;; (person:age obj) and they access distinct slots.
;;
;; If you *do* want the AGE slot to be shared by the superclasses, you
;; should put the DEFCLASS forms in the same package.
;;;-----------------------------

;;; @@PLEAC@@_13.13
;;;-----------------------------
(setf (next node) node)
;;;-----------------------------
(defclass node ()
  ((next :accessor next :type node)
   (prev :accessor prev :type node)
   (value :accessor value)))

(defclass ring ()
  ((dummy :accessor dummy :type node)
   (ring-count :accessor ring-count :type number :initform 0)))

(defmethod initialize-instance :after ((self ring) &rest init-args)
  (declare (ignore init-args))
  (let ((dummy (make-instance 'node)))
    (setf (next dummy) dummy
          (prev dummy) dummy
          (value dummy) 'dummy)         ; so PRINT-OBJECT works
    (setf (dummy self) dummy)))
;;;-----------------------------
(loop for i below 20
   do (let ((r (make-instance 'ring)))
        (loop repeat 1000
           do (insert r i))))
;;;-----------------------------
;; Note unlike Perl this isn't called automatically by CL when the
;; object is collected, however some CL implementations provide an
;; extension that could be used to "register" this function as such.
;; Also, depending on the sophistication of your implementation's
;; garbage collector, it might not be necessary to even have this
;; method (since the object doesn't hang on to any external
;; resources).
(defmethod destroy ((ring ring))
  (loop for node = (next (dummy ring)) then (next node)
       until (eq node (dummy ring))
       do (delete-node ring node))
  (setf (prev (dummy ring)) nil
        (next (dummy ring)) nil))
       
(defmethod delete-node ((ring ring) (node node))
  (setf (next (prev node)) (next node)
        (prev (next node)) (prev node))
  (decf (ring-count ring)))
;;;-----------------------------
(defmethod ring-search ((ring ring) value &key (test 'eql))
  "Find VALUE in the RING structure, returning a NODE."
  (loop 
     for node = (next (dummy ring)) then (next node)
     until (eq node (dummy ring))
     do (when (funcall test (value node) value)
          (return-from ring-search node)))
  (dummy ring))

(defmethod insert ((ring ring) value)
  "Insert VALUE into the RING structure."
  (let ((node (make-instance 'node)))
    (setf (value node) value
          (next node) (next (dummy ring))
          (prev (next (dummy ring))) node
          (next (dummy ring)) node
          (prev node) (dummy ring))
    (incf (ring-count ring))))

(defmethod delete-value ((ring ring) value &key (test 'eql))
  "Delete a node from the RING structure by VALUE."
  (let ((node (ring-search ring value :test test)))
    (unless (eq node (dummy ring))
      (delete-node ring node))))

;; just for debugging
(defmethod print-object ((node node) stream)
  (format stream "#<NODE ~A>" (value node)))

(defmethod print-object ((ring ring) stream)
  (format stream "#<RING")
  (do ((node (next (dummy ring)) (next node)))
      ((eq node (dummy ring)))
    (format stream " ~A" node))
  (format stream ">"))
;;;-----------------------------

;;; @@PLEAC@@_13.14
;;;-----------------------------
;; In CL there is no distinction between operators and functions.
;; However, functions can be "overloaded" by using DEFMETHOD.

(defmethod <=> ((s1 hash-table) (s2 hash-table))
  (let ((s1 (gethash 'name s1))
        (s2 (gethash 'name s2)))
    (case (string-lessp s1 s2)
      (nil (if (string= s1 s2)
               0
               1))
      (t -1))))

;; TODO: write a reader macro for "-like stuff.
;;;-----------------------------
;; The following allows us to implement MY-PLUS without having to
;; prefix most of the function calls, etc, with CL:, which gets
;; old fast.
(defpackage :time-number-internal (:use cl))

;; The following package is where we define the method.  It doesn't
;; use the CL package, so that + can be redefined (redefining anything
;; in the CL package is disallowed by the standard).
(defpackage :time-number)

(in-package time-number)

(cl:defclass time-number ()
  ((seconds :accessor seconds :initform 0 :initarg :seconds)
   (minutes :accessor minutes :initform 0 :initarg :minutes)
   (hours :accessor hours :initform 0 :initarg :hours)
   ))

(cl:defmethod + ((left time-number) (right time-number))
  (time-number-internal::my-plus left right))

(cl:export 'time-number)

;; There's no need to do anything like "use overload", you can just
;; use DEFMETHOD directly, as shown below.
;;;-----------------------------
(in-package time-number-internal)

;; The following can safely assume that LEFT and RIGHT are TIME-NUMBER
;; objects, since this is only called by the TIME-NUMBER:+ method
;; specialized on TIME-NUMBER.
(defun my-plus (left right)
  (let ((answer (make-instance 'time-number:time-number)))
    (with-accessors ((answer-seconds seconds) (answer-minutes minutes) (answer-hours hours)) answer
      (setf answer-seconds (+ (seconds left)
                              (seconds right))
            answer-minutes (+ (minutes left)
                              (minutes right))
            answer-hours (+ (hours left)
                            (hours right)))
      (when (>= answer-seconds 60)
        (setf answer-seconds (mod answer-seconds 60))
        (incf answer-minutes))
      (when (>= answer-minutes 60)
        (setf answer-minutes (mod answer-minutes 60))
        (incf answer-hours)))
    answer))
;;;-----------------------------
;;; @@INCOMPLETE@@
;;;-----------------------------

;;; @@PLEAC@@_18.1
;;;-----------------------------
(use-package :sb-bsd-sockets)
(defparameter *addresses* (host-ent-addresses (get-host-by-name name)))
;; *addresses* is a list of IP addresses (#(192 48 96 9) #(192 48 96 23))
;;;-----------------------------
(defun inet-ntoa (packed-address)
  (format nil "~{~D~^.~}" (coerce packed-address 'list)))

(defparameter *address* (inet-ntoa (make-inet-address *name*)))
;;;-----------------------------
(use-package :sb-bsd-sockets)
(defparameter *name* (get-host-by-address #(192 48 96 9)))
;; *NAME* is the hostname "ftp.uu.net"
;;;-----------------------------
(use-package :sb-bsd-sockets)
(defparameter *packed-address* (make-inet-address "208.146.140.1"))
(defparameter *ascii-address* (inet-ntoa *packed-address*)) ; INET-NTOA defined above
;;;-----------------------------
(use-package :sb-bsd-sockets)
(let* ((packed (get-host-by-name *hostname*)) ; no need for die, will signal a condition
       (address (host-ent-address packed)))
  (format t "I will use ~A as the address for ~A~%" address *hostname*))
;;;-----------------------------
;; *address* is the IP address I'm checking, like "128.138.243.20"
(use-package :sb-bsd-sockets)
(defparameter *name* (host-ent-name 
                      (get-host-by-address (make-inet-address *address*))))
(defparameter *addresses* (host-ent-addresses (get-host-by-name *name*)))
(defparameter *found* (member *address* *addresses* :test 'equal :key 'inet-ntoa))
;;;-----------------------------
;;; @@INCOMPLETE@@
;;;-----------------------------