Skip to content

yitzchak/hoop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

62 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HOOP

HOOP is an iteration facility for Common Lisp that is conceptually similar to the builtin LOOP facility. Unlike LOOP it separates clauses that create bindings or iteration from body forms which LOOP calls unconditional, conditional or selectable clauses.

HOOP is still experimental. It does not have much type checking yet. It has all of the functionality of LOOP and some additional functionality such as the ability to update values in lists or sequences. Generally there is a simple translation between LOOP and HOOP. For example using LOOP to collect generate '((1 . a) (3 . c) (5 . e)) could be done with:

(loop for i from 1
      for j in '(a b c d e)
      when (oddp i)
        collect (cons i j))

Using HOOP this can be done with:

(hoop* ((:step i :from 1)
        (:each-item j :in '(a b c d e))
        (:collect c))
  (when (oddp i)
    (c (cons i j))))

Overall HOOP Syntax

([hoop | hoop*]
    ((:step var &key from to before by)
     (:each-item var &key in by update)
     (:each-sublist var &key in by update)
     (:each-elt var &key in by start end index update)
     (:each-key-value (key value) &key in)
     (:each-symbol var &key in symbol-types status package)
     (:generate var &key using then)
     (:with var &key =)
     (:collect var &key into)
     (:count var)
     (:minimize var)
     (:maximize var)
     (:sum var)
     (:product var)
     (:while form)
     (:until form)
     (:always form)
     (:never form)
     (:thereis form)
     (:repeat form)
     (:named symbol)
     (:prologue form*)
     (:epilogue form*)
     (:before form*)
     (:after form*)
     (:parallel clause*)
     (:serial clause*))
  declaration*
  tagbody-form*)

Differences between LOOP and HOOP

  1. HOOP places all of the iteration clauses in the first argument position like LET.

  2. Accumulation clauses are declared in the "binding" group, but called in the body via functions declared with FLET. The currect value of the accumulation can be accessed with a variable of the same name.

  3. Various termination clauses can be placed in the "binding" group or HOOP-FINISH, HOOP-NEXT, or RETURN can be called in the body.

  4. There are no unconditional, conditional or other selectable clauses. These are left as ordinary Lisp forms in a body as in LET.

  5. There is no parsing of the body or of the binding forms. The binding and iteration forms are typically passed to the appropriate MAKE-INSTANCE for the clause type. What appears to be keywords are usually just key/value pairs.

  6. There is a parallel binding version (HOOP) and a serial binding version (HOOP*). One can also switch between parallel and serial with the :parallel or :serial clauses.

The overall syntax of HOOP with serial bindings is

(hoop* (clause*)
  body)

Using HOOP versus HOOP* will use parallel bindings.

The various clauses are described below.

Step Clause

(:step simple-var &key (from 0) to before (by 1))
  • :from specifies the value from which stepping begins.
  • :to marks the end value for stepping.
  • :before marks the end value for stepping like to but iteration is terminated before simple-var reaches the value.
  • :by marks the increment or decrement. If a negative value is used then simple-var will be decremented.

Basic Examples

CL-USER> (hoop ((:step i :from 1 :to 3))
           (print i))

1 
2 
3 
NIL
CL-USER> (hoop ((:step i :from 10 :to 1 :by -3))
           (print i))

10 
7 
4 
1 
NIL
CL-USER> (hoop ((:step i :before 3 :by -1))
           (print i))
NIL
CL-USER> (hoop ((:step i :before 3))
           (print i))

0 
1 
2 
NIL

List Item Clause

(:each-item d-var-spec &key in (by #'cdr) update)

Iterates over the contents of a list..

  • :in specifies the list form to iterate over. d-var-spec is applied to the CAR of each list CONS.
  • :by specifies the function to advance the iteration.
  • :update causes SETF applied to d-var-spec to set lisp contents via symbol macros.

If d-var-spec is a CONS then destructuring happens in the same way as LOOP.

Examples

CL-USER> (hoop ((:each-item i :in '(1 2 3)))
           (print i))

1 
2 
3 
NIL
CL-USER> (hoop ((:each-item i :in '(1 2 3 4 5) :by #'cddr))
           (print i))

1 
3 
5 
NIL
CL-USER> (hoop ((:each-item (i . x) :in '((A . 1) (B . 2) (C . 3)))
                (:sum j))
           (unless (eq i 'B)
             (j x)))
4
CL-USER> (defparameter a (list 1 2 3 4 5))
A
CL-USER> (hoop ((:each-item i :in a :update t))
           (setf i (list i (if (oddp i) :odd :even))))
NIL
CL-USER> a
((1 :ODD) (2 :EVEN) (3 :ODD) (4 :EVEN) (5 :ODD))

List Sublist Clause

(:each-sublist d-var-spec &key in (by #'cdr))

Iterates over the sublists of a list specified by :in.

  • :in specifies the list form to iterate over. d-var-spec is applied to each list CONS.
  • :by specifies the function to advance the iteration.
  • :update causes SETF applied to d-var-spec to set lisp contents via symbol macros.

If d-var-spec is a CONS then destructuring happens in the same way as LOOP.

Examples

CL-USER> (hoop ((:each-sublist sublist :in '(a b c d))
                (:collect j))
           (j sublist))
((A B C D) (B C D) (C D) (D))
CL-USER> (hoop ((:each-sublist (i) :in '(1 2 3)))
           (print i))

1 
2 
3 
NIL

Generate Clause

(:generate d-var-spec &key using then)

Initializes d-var-spec by setting it to the result of evaluating using on the first iteration, then setting it to the result of evaluating then on the second and subsequent iterations. If :then is omitted, the construct uses using on the second and subsequent iterations.

If d-var-spec is a list then multiple-value-setq will be used to assign to the names in d-var-spec. Each item in this list will be treated as a separate d-var-spec.

Examples

CL-USER> (hoop ((:generate i :using 1 :then (+ i 10))
                (:step j :from 1 :to 5)
                (:collect k))
           (k i))
(1 11 21 31 41)
CL-USER> (hoop ((:each-item i :in '(:fu :bar :wibble))
                (:with j := '(:bar 2 :quux 743 :fu 27))
                (:generate (indicator value nil)
                 :using (get-properties j (list i))))
           (when indicator
             (format t "Found ~s with a value of ~s~%"
                     indicator value)))
Found :FU with a value of 27
Found :BAR with a value of 2

Each Element Clause

(:each-elt d-var-spec &key in by start end)

Uses elt to iterate over the elements of a sequence given by the :in key. The range of indicies and the stepping between successive indicies can be controlled with the :start, :end and :by keys.

Example

CL-USER> (hoop ((:each-elt i :in "abcd"))
           (print i))

#\a 
#\b 
#\c 
#\d 
NIL

Each Key Value Clause

(:each-key-value (d-var-spec d-var-spec) &key in)

Iterates over a hash table supplied by the :in key.

Examples

CL-USER> (defparameter a (make-hash-table))
A
CL-USER> (setf (gethash :fu a) 1)
1 (1 bit, #x1, #o1, #b1)
CL-USER> (setf (gethash :bar a) 2)
2 (2 bits, #x2, #o2, #b10)
CL-USER> (hoop ((:each-key-value (k v) :in a))
           (format t "~s ~s~%" k v))
:FU 1
:BAR 2
NIL
CL-USER> (hoop ((:each-key-value (k nil) :in a))
           (print k))

:FU 
:BAR 
NIL

Each Symbol Clause

(:each-symbol simple-var &key in symbol-types package status)

Examples

CL-USER> (make-package "FU")
#<PACKAGE "FU">
CL-USER> (intern "BAR" 'FU)
FU::BAR
NIL
CL-USER> (export 'fu::bar 'fu)
T
CL-USER> (intern "QUUX" 'FU)
FU::QUUX
NIL
CL-USER> (hoop* ((:each-symbol s :in 'fu
                  :package p :status stat
                  :symbol-types (:external :internal)))
           (format t "~s ~s ~s~%" s p stat))
FU::QUUX #<PACKAGE "FU"> :INTERNAL
FU:BAR #<PACKAGE "FU"> :EXTERNAL
NIL

With Clause

(:with simple-var &key =)
;; These bindings occur in sequence.
CL-USER> (hoop* ((:with a := 1)
                 (:with b := (+ a 2))
                 (:with c := (+ b 3)))
           (return (list a b c)))
(1 3 6)
CL-USER> (defparameter a 5)
A
CL-USER> (defparameter b 10)
B
;; These bindings occur in parallel.
CL-USER> (hoop* ((:parallel (:with a := 1)
                            (:with b := (+ a 2))
                            (:with c := (+ b 3))))
           (return (list a b c)))
(1 7 13)

Collect Clause

(:collect simple-var &key into)
;; Collect all the symbols in a list.
CL-USER> (hoop* ((:each-item i :in '(bird 3 4 turtle (1 . 4) horse cat))
                 (:collect c))
           (when (symbolp i)
             (c i)))
(BIRD TURTLE HORSE CAT)
;; Collect and return odd numbers.
CL-USER> (hoop* ((:step i :from 1 :to 10)
                 (:collect c))
           (when (oddp i)
             (c i)))
(1 3 5 7 9)
;; Collect items into local variable
CL-USER> (hoop* ((:each-item i :in '(a b c d) :by #'cddr)
                 (:collect my-list))
           (my-list i))
(A C)
;; Use APPEND to concatenate some sublists.
CL-USER> (hoop* ((:each-item x :in '((a) (b) ((c))))
                 (:collect c))
           (c x :append))
(A B (C))
;; NCONC some sublists together.  Note that only lists made by the
;; call to LIST are modified.
CL-USER> (hoop* ((:step i)
                 (:each-item x :in '(a b (c)))
                 (:collect c))
           (c (if (evenp i)
                  (list x)
                  nil)
              :nconc))
(A (C))

Count Clause

(:count simple-var)
CL-USER> (hoop* ((:each-item i :in '(a b nil c nil d e))
                 (:count c))
           (c i))
5

Minimize Clause

(:minimize simple-var)
CL-USER> (defparameter series '(1.2 4.3 5.7))
SERIES
CL-USER> (hoop* ((:each-item v :in series)
                 (:minimize c))
           (c (round v)))
1

Maximize Clause

(:maximize simple-var)
CL-USER> (defparameter series '(1.2 4.3 5.7))
SERIES
CL-USER> (hoop* ((:each-item v :in series)
                 (:maximize c))
           (c (round v)))
6

Sum Clause

(:sum simple-var)
CL-USER> (hoop* ((:each-item i :in '(1 2 3 4 5))
                 (:sum c))
           (c i))
15
CL-USER> (defparameter series '(1.2 4.3 5.7))
SERIES
CL-USER> (hoop* ((:each-item v :in series)
                 (:sum c))
           (c (* 2.0 v)))
22.4

Repeat Clause

(:repeat count-form)
CL-USER> (hoop* ((:repeat 3))
           (format t "~&What I say three times is true.~%"))
What I say three times is true.
What I say three times is true.
What I say three times is true.
NIL
CL-USER> (hoop* ((:repeat -15))
           (format t "What you see is what you expect~%"))
NIL

Releases

No releases published

Sponsor this project

 

Packages

No packages published