Iterators: Signs of Weakness in Object-Oriented Languages (By Henry G Baker, 1992)

By Xah Lee. Date: .

Iterators: Signs of Weakness in Object-Oriented Languages

[1]Henry G. Baker

A. INTRODUCTION

B. MAPPING FUNCTIONS

(defun mapc (function list) ; [3][2] tail-recursive loop.
  (if (null list) nil
    (progn (funcall function (car list))
           (mapc function (cdr list)))))

For those people find it more productive to think of an iterative process instead of a mapping process, Common Lisp provides the macro dolist. The following fragment shows two different ways to print all of the elements of a list.

(dolist (x l) (print x))  ; prints elements of list l.

(mapc #'(lambda (x) (print x)) l) ; or more simply (after eta conversion),

(mapc #'print l)   ; prints elements of list l.
(defmacro dolist ((var list) &body forms)  ; approximation to CL 'dolist'.
  `(mapc #'(lambda (,var) ,@forms) ,list))

Mapping functions are not limited to Lisp lists, as our definition of the Lisp dotimes iteration macro shows.

(defmacro dotimes ((var limit) &body forms) ; [4][3]
  `(maptimes #'(lambda (,var) ,@forms) ,limit))

(defun maptimes (function n)
  (labels
    ((myloop (i)    ; local tail-recursive function to perform loop.
       (if (= i n) nil
         (progn (funcall function i)
                (myloop (1+ i))))))
    (myloop 0)))

(maptimes #'print 10) ; prints numbers from 0 through 9.

(dotimes (i 10) (print i)) ; prints numbers from 0 through 9.

It should be clear that the more complicated iteration forms of Pascal, C, C++, etc., can all be emulated in the same fashion using mapping functions.

C. GENERATORS AND CO-ROUTINES

(defun fringe (x &optional (c nil)) ; fringe of tree x.
 (if (atom x) (cons x c) (fringel x c)))

(defun fringel (xl c)    ; fringe of list of trees xl.
  (if (null xl) c (fringe (car xl) (fringel (cdr xl) c))))

(defun samefringe1 (x y)   ; construct both fringe(x) and fringe(y).
  (equal (fringe x) (fringe y)))

(defun samefringe2 (x y)   ; construct only fringe(x).
  (cfringe (fringe x) y #'(lambda (lx) (null lx))))

(defun cfringe (lx y c)
  (if (atom y) (and (consp lx) (eql (car lx) y) (funcall c (cdr lx)))
    (cfringel lx y c)))

(defun cfringel (lx yl c)
  (if (null yl) (funcall c lx)
    (cfringe lx (car yl) #'(lambda (lx) (cfringel lx (cdr yl) c)))))
(defun eof (c) (funcall c nil t nil)) ; empty generator, send eof flag.

(defun samefringe (x y)    ; "lazy" samefringe.
  (samefringec #'(lambda (c) (genfringe x c #'eof))
               #'(lambda (c) (genfringe y c #'eof))))

(defun samefringec (xg yg)  ; check equality of leaves generated by genfringe.
  (funcall xg ; We don't need Scheme 1st class continuations.
   #'(lambda (x eofx xg) ; receive 1st elt., eof flag, & generator for rest.
       (funcall yg
        #'(lambda (y eofy yg)
            (or (and eofx eofy)  ; equal if both reach eof simultaneously.
                (and (eql x y) (samefringec xg yg))))))))

(defun genfringe (x consumer genrest)  ; call consumer with leaves found in x.
  (if (atom x) (funcall consumer x nil genrest) ; send 1st elt. & ~eof flag.
    (genfringel x consumer genrest)))

(defun genfringel (xl consumer genrest)
  (if (null xl) (funcall genrest consumer)
    (genfringe (car xl) consumer
     #'(lambda (consumer) (genfringel (cdr xl) consumer genrest)))))
(defmacro lcons (x y) ; a cons which is lazy in its second argument.
  `(cons ,x #'(lambda () ,y)))

(defmacro lcdr (x) `(funcall (cdr ,x)))

(defmacro fringe (x &optional (c nil))  ; make fringe lazy in its 2nd arg.
  `(lfringe ,x #'(lambda () ,c))) ; this macro must appear before fringel.

(defun lfringe (x lc)
  (symbol-macrolet ((c '(funcall lc))) ; accessing 'c' forces arg. evaluation.
    (if (atom x) (lcons x c)  ; cdr of new cons is unevaluated argument.
      (fringel x c))))     ; fringel is defined above.

(defun lequal (x y)     ; works only for 1-level list with lazy cdr.
  (or (and (null x) (null y))
      (and (consp x) (consp y)
           (eql (car x) (car y)) (lequal (lcdr x) (lcdr y)))))

(defun lsamefringe (x y) (lequal (fringe x) (fringe y)))

D. ITERATORS

int i,first(void),next(void); boolean last(void);
for (i=first(); !last(); i=next()) {printf("%d\n",i);}

E. EMULATING MAPPING IN C AND C++

Before we can show how mapping can replace iterators, we first explore what goes wrong when we attempt to program a mapping function — e.g. maptimes — in C. We first show maptimes itself.

void maptimes(fn,limit) void (*fn)(int); int limit;
{int i; for (i=0; i<limit; i++) (*fn)(i);}
static void myprint(i) int i; {printf("%d\n",i);}
...; maptimes(&amp;amp;amp;myprint,10); ...
Already, we can see one problem of C — it has no “anonymous” functions, so we have to come up with a name for a function, define the function with that name, and then use this name in the call to maptimes, even though the function is used only once. This problem is an irritation, but not a show-stopper.
int a[10] = { ... } /* vector of int's. */
...; {int sum=0; for (i=0; i<10; i++) sum+=a[i]; printf("sum=%d\n",sum);} ...
int a[10] = { ... }

static void myloop(i) int i; {sum+=a[i];}
...; {int sum=0; maptimes(&myloop,10); printf("sum=%d\n",sum);} ...
#define dotimes(var,limit,body) {int var; for(var=0; var<limit; var++) body;}

...; {int sum=0; dotimes(i,10,{sum+=a[i];}); printf("sum=%d\n",sum);} ...

F. MAPPING 'FUNCTIONS' IN ADA83

generic type element_type is private;       -- Element type is generic formal.
package collection_package is
  type collection is private;             -- Define a private collection type.
  generic with procedure body(e: element_type);
    procedure iterate(c: collection);
  pragma inline(iterate);
private ...
  end collection_package;

with collection_package;
procedure main is
  -- Define or inherit 'my_element_type' type.
  package my_collection is new collection_package(my_element_type);
  local_list: my_collection.collection;
  begin
    ...
    declare    -- The lack of anonymous procedures in Ada is truly irritating.
      procedure iterate_body1(e: my_element_type) is ... ;
      procedure iteration1 is new my_collection.iterate(interate_body1);
      pragma inline(iterate_body1,iteration1);
      begin
        iteration1(local_list); -- iterate over local list of elements.
      end;
    ...
  end main;

G. WHY USE MAPPERS INSTEAD OF ITERATORS?

H. FUNCTIONAL ITERATION PROTOCOLS

(defun eof (c) (funcall c nil t nil)) ; same as in section C.

(defmethod gen ((x tree) consumer genrest)
  (labels ((genfringel (xl consumer)
             (if (null xl) (funcall genrest consumer)
               (gen (the tree (car xl)) consumer
                #'(lambda (consumer) (genfringel (cdr xl) consumer))))))
    (if (atom x) (funcall consumer x nil genrest)
      (genfringel x consumer))))

(defmethod gen ((x list) consumer genrest)
  (if (null x) (funcall genrest consumer)
    (funcall consumer (car x) nil
     #'(lambda (consumer) (gen (the list (cdr x)) consumer genrest)))))

(defmethod gen ((x vector) consumer genrest)
  (labels ((myloop (i consumer)
             (if (= i (length x)) (funcall genrest consumer)
               (funcall consumer (aref x i) nil
                #'(lambda (consumer) (myloop (1+ i) consumer))))))
    (myloop 0 consumer)))

(defun every (fn x y) ; [10][8] true only if all fn applications are true.
  (labels
   ((myloop (xg yg)
     (funcall xg
      #'(lambda (x eofx xg)
         (funcall yg
          #'(lambda (y eofy yg)
             (or (and eofx eofy) (and (funcall fn x y) (myloop xg yg)))))))))
   (myloop #'(lambda (c) (gen x c #'eof)) #'(lambda (c) (gen y c #'eof)))))

(defun samefringe (x y) (every #'eql x y)) ; example of every's use.

(defun mapcar (fn x y)  ; accepts 2 generic sequences, returns list of values.
  (labels
   ((myloop (xg yg)
     (funcall xg
      #'(lambda (x eofx xg)
         (funcall yg
          #'(lambda (y eofy yg)
             (if (or eofx eofy) nil
               (cons (funcall fn x y) (myloop xg yg)))))))))
   (myloop #'(lambda (c) (gen x c #'eof)) #'(lambda (c) (gen y c #'eof)))))

I. CONCLUSIONS

J. REFERENCES

References

Source

also at https://dl.acm.org/doi/10.1145/165507.165514

plain text copy Iterators_Signs_of_Weakness_in_Object_Oriented_Languages__Henry_G_Baker__1992.txt

Object Oriented Programing Shams