فهرست منبع

Added iterators CL library and test

Added CL iterators library with test.  Library uses closures.
Samuel W. Flint 10 سال پیش
والد
کامیت
352a3067f0
2فایلهای تغییر یافته به همراه132 افزوده شده و 0 حذف شده
  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)))