Browse Source

Added iterators CL library and test

Added CL iterators library with test.  Library uses closures.
Samuel W. Flint 9 years ago
parent
commit
352a3067f0
2 changed files with 132 additions and 0 deletions
  1. 81 0
      iterators.lisp
  2. 51 0
      test-of-iterators.lisp

+ 81 - 0
iterators.lisp

@@ -0,0 +1,81 @@
+;;;; iterators.lisp
+
+(defpackage #:iterators-simple
+  (:use :cl)
+  (:export #:with-iterator
+           #:call-iterator
+           #:define-iterator
+           #:create-iterator))
+
+(in-package #:iterators-simple)
+
+;;; "iterators" goes here. Hacks and glory await!
+
+(defun make-numeric-iterator (function second-argument &optional (initial 1))
+  "Create a closure acting as a numeric iterator.
+This takes a FUNCTION, a SECOND-ARGUMENT to a function,
+and an INITIAL value for N defaulting at 1.
+
+This applies the function to N, and the SECOND-ARGUMENT,
+setting N to the value."
+  (let ((n initial))
+    #'(lambda ()
+        (setf n (funcall function n second-argument)))))
+
+(defun make-stream-iterator (stream-or-file-name type)
+  (let ((stream (if (stringp stream-or-file-name)
+                    (open stream-or-file-name)
+                    stream-or-file-name)))
+    (ecase type
+      (:line
+       #'(lambda ()
+           (read-line stream nil nil)))
+      (:char
+       #'(lambda ()
+           (read-char stream nil nil)))
+      (:byte
+       #'(lambda ()
+           (read-byte stream nil nil))))))
+
+(defun make-list-iterator (&rest list)
+  (let ((list list))
+    #'(lambda ()
+        (if (= 0 (length list))
+            nil
+            (pop list)))))
+
+(defun call-iterator (iterator)
+  (funcall iterator))
+
+(defun create-iterator (type &rest args)
+  "Create an integer of type TYPE, using args ARGS."
+  (ecase type
+    (:numeric
+     (make-numeric-iterator (elt args 0) (elt args 1) (if (elt args 2) (elt args 2) 1)))
+    (:stream
+     (make-stream-iterator (elt args 0) (elt args 1)))
+    (:list
+     (apply #'make-list-iterator args))))
+
+(defmacro with-iterator ((name type &rest args) &body body)
+  (let ((iterator-expression (ecase type
+                               (:numeric
+                                `(make-numeric-iterator ,(elt args 0) ,(elt args 1) ,(if (elt args 2) (elt args 2) 1)))
+                               (:stream
+                                `(make-stream-iterator ,(elt args 0) ,(elt args 1)))
+                               (:list
+                                `(make-list-iterator ,@args)))))
+    `(let ((,name ,iterator-expression))
+       (flet ((,name ()
+                (call-iterator ,name)))
+         ,@body))))
+
+(defmacro define-iterator (name type &rest args)
+  (let ((iter-expression (ecase type
+                           (:numeric
+                            `(make-numeric-iterator ,(elt args 0) ,(elt args 1) ,(if (elt args 2) (elt args 2) 1)))
+                           (:stream
+                            `(make-stream-iterator ,(elt args 0) ,(elt args 1)))
+                           (:list
+                            `(make-list-iterator ,@args)))))
+    `(defvar ,name ,iter-expression)))

+ 51 - 0
test-of-iterators.lisp

@@ -0,0 +1,51 @@
+;;;; test-of-iterators.lisp
+
+(load "iterators.lisp")
+
+(defpackage #:iterators-simple.tests
+  (:use :cl
+        :iterators-simple)
+  (:export #:number-lines
+           #:count-lines
+           #:count-characters))
+
+(in-package #:iterators-simple.tests)
+
+;;; "test-of-iterators" goes here. Hacks and glory await!
+
+(defun number-lines (file &optional (output-stream t))
+  "Print each line prepended with its number."
+  (let ((n 1)
+        (get-line (create-iterator :stream file :line)))
+    (flet ((get-line ()
+             (call-iterator get-line)))
+      (loop for line = (get-line)
+         if (not line) do (return)
+         else
+         do (progn
+              (format output-stream "~4d: ~a~%" n line)
+              (incf n 1))))))
+
+(defun number-of-lines (file)
+  "Count the number of lines in a file."
+  (let ((n 0)
+        (get-line (create-iterator :stream file :line)))
+    (flet ((get-line ()
+             (call-iterator get-line)))
+      (loop for line = (get-line)
+         if (not line) do (return)
+         else
+         do (incf n 1))
+      n)))
+
+(defun count-characters (file)
+  "This counts the number of characters in a file."
+  (let ((n 0)
+        (get-character (create-iterator :stream file :char)))
+    (flet ((get-character ()
+             (call-iterator get-character)))
+      (loop for char = (get-character)
+         if (not char) do (return)
+         else
+         do (incf n 1))
+      n)))