File: clsdemo.lsp

package info (click to toggle)
xlispstat 3.52.14-1
  • links: PTS
  • area: main
  • in suites: potato
  • size: 7,560 kB
  • ctags: 12,676
  • sloc: ansic: 91,357; lisp: 21,759; sh: 1,525; makefile: 521; csh: 1
file content (1006 lines) | stat: -rw-r--r-- 33,676 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
; Demonstration of classes in XLISP by implementing various Smalltalk
; Collection classes.
; Author: Tom Almy
; Date:   September 1996

; NOTE -- you should probably check out EXAMPLE.LSP, TURTLES.LSP, and
; BLOCKS.LSP first as they are somewhat simpler.

#-:classes (load "classes")	; We'll use these nice macros

; We will put everyting in a package to keep it out of the user name space

#+:packages (unless (find-package "CLASSES")
		    (make-package "CLASSES" :use '("XLISP")))

(in-package "CLASSES")

; List the symbols available on the outside -- in this case the class names.
; The message selectors are all in the keyword package so they don't need
; to be exported.

(export '(Collection Set Bag Dictionary SequenceableCollection
          Array OrderedCollection SortedCollection Interval))
		     


; Our basic Collection class is "abstract" -- it's just defined to
; subclass into useful types of collections. We'll define a single instance
; variable: "data" contains the collection's data, the format to be
; defined by the subclass. Various subclasses will define any additional
; instance variables.

; The actual collections used in applications will be created from subclasses
; of Collection. This demo will implement:

;  Bag -- an unordered collection of objects
;  Set -- like a bag, but no duplicate elements
;  Dictionary -- access elements using symbolic keys
;  SequenceableCollection -- Abstract class which is subclassed into:
 ;  Array -- elements have a sequence. Collection has a fixed size
 ;  OrderedCollection -- same as Array but no fixed size, can add/delete from
 ;         either end.
   ;  SortedCollection -- An Ordered collection with a colating sequence
 ;  Interval -- contains a constant sequence


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                               ;
;        THE COLLECTION CLASS                                   ;
;                                                               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defclass Collection (data))

; The defclass macro defines a new class, which is bound to the symbol
; "Collection". The macro also defines several default methods for instance
; variable access (:data in this case), and instance initialization (:isnew).

; Unlike Smalltalk, XLISP has no class methods. In Smalltalk you create an
; instance of a class by sending a message to the class. In XLISP, the classes
; are members of class Class, and you create an instance by sending the class
; a message with the selector :new. i.e. (send MyClass :new <xxx>) where xxx
; are 0 or more arbitrary expressions. This executes the :new method in class
; Class which will create an object which is a member of the desired class
; (call it newobj) and then does a (send newobj :isnew <xxx>). The :isnew
; method gets the expressions, which it can then use to customize the
; initiation. So, basically, the :isnew method in a class takes the place of
; the class methods of Smalltalk. Not as functional, but it usually does all
; that is needed. The class Array demonstrates how two instance creation
; methods can be supported via the use of keyword arguments.

;;;;;;;;;
; The following group of methods are "private" methods in that they are not
; intended for use in applications, but just to aid in implementation.

; :notImplemented provides a nice error message for messages that aren't
; handled by our class. It's not really necessary to define these!

(defmethod Collection :notImplemented (msg)
	   (error "~s not handled in class ~a"
		  msg
		  (send (send self :class) :pname)))

; Here's the difference:
; >(send x :foo)                                    :notImplemented USED
; error: no method for this message - :foo 

; >(send x :foo)                                :notImplemented NOT USED
; error: :foo not handled in class Bag

; :map is a mapcar like mapping function for the collection.
; This version only works when data instance variable is sequence of
; collection elements. We will have to override the method for subclasses
; that maintain their data differently.

(defmethod Collection :map (fcn) (map 'cons fcn data))

; :addAll will add the elements in the argument collection to this
; collection. We'll extend this definition so it works with sequences as
; well. It won't work with Arrays (which are a fixed size), Intervals
; (which are not alterable), or Dictionaries (which require keys).

(defmethod Collection :addAll (arg)
	   (if (or (listp arg) (arrayp arg) (stringp arg))
	       ; Use map when argument is a sequence
	       (map nil (lambda (x) (send self :add x)) arg)
	       ; Otherwise, send :map to the argument collection
	       (send arg :map (lambda (x) (send self :add x))))
	   self)

; Override default "isnew" to disallow creating abstract collections.
; There is no reason for any program to create an instance of Collection.

(defmethod Collection :isnew (&rest dummy)
	   (error "Don't create collections of class \"Collection\""))

;;;;;;;;;
; Now we will define some "public" methods for Collection. Most will be
; overriden in a subclass. The rest we will provide with a common default
; functionality.


; :prin1 determines how an object is printed. The default is to print
; the objects class and unique ID. We want to do better than that if the
; collection is small enough to easily display, say 5 or fewer elements

(defmethod Collection :prin1 (&optional (stream *standard-output*))
	   (let ((contents (send self :asList)) ; get collection as a list
		 (cls(send(send self :class):pname))) ; and get our class' name
		(cond ((null contents)
		       (format stream "#<An empty ~a>" cls))
		      ((< (length contents) 6)
		       (format stream "#<~a:~{ ~s~}>" cls contents))
		      (t
		       (format stream "#<~a:~5{ ~s~} ...>" cls contents)))))
		       

; :storeon is used to create an expression which, when executed, will create a
; copy of the object. The Default method, part of class Object, won't work
; for classes that override :isnew, and all Collection classes do.

(defmethod Collection :storeon ()
	   (list 'send
		 (list 'send
		       (intern (send (send self :class) :pname))
		       :new)
		 :addAll
		 (list
		  'quote
		  (send self :asList))))

; :at will fetch an element from an "sequenceable collection"
; Not all collections have the concept of sequencing.

(defmethod Collection :at (arg) (send self :notImplemented :at))

; :atput will store an element into a "sequenceable collection".

(defmethod Collection :atPut (arg1 arg2) (send self :notImplemented :atPut))

; :first will fetch the first element of the collection, where appropriate.
; :last does the same thing but for the last element.

(defmethod Collection :first () (send self :notImplemented :first))
(defmethod Collection :last () (send self :notImplemented :last))

; :add will store (one or more copies of) an element into a collection
; :addFirst will add to the start of a collection. These two are not
; implemented for all classes.

(defmethod Collection :add (arg &optional value)
	   (send self :notImplemented :add))

(defmethod Collection :addFirst (arg) 
	   (send self :notImplemented :addFirst))

(defmethod Collection :addLast (arg) 
	   (send self :notImplemented :addLast))

; Delete the specified, first, or last element

(defmethod Collection :remove (arg)
	   (send self :notImplemented :remove))

(defmethod Collection :removeFirst ()
	   (send self :notImplemented :removeFirst))

(defmethod Collection :removeLast ()
	   (send self :notImplemented :removeLast))

; :size -- Get the size of the the Collection. This will work for
; most subclasses.

(defmethod Collection :size () (length data))

; :empty -- Returns T if collection has no elements

(defmethod Collection :empty () (zerop (send self :size)))

; :includes tells us if a object is a member of the collection
; This version only works when data instance variable is sequence of
; collection elements

(defmethod Collection :includes (arg)
	   (if (position arg data) t nil))

; :species returns the class similar to the current class to create new
; objects

(defmethod Collection :species ()
	   (send self :class))


; :do is like :map but returns nothing
; :collect is like :map, but returns a new collection.
; :select returns a collection of elements for which the predicate function
;    returns non-NIL.
; These are generic enough to work for any of the Collection subclasses
; except Array, which requires an argument to :new,
; however in many cases they could be overridden for speed.
; Smalltalk defines these and a number of similar functions.

(defmethod Collection :do (fcn) (send self :map fcn) nil)

(defmethod Collection :collect (fcn)
	   (send (send (send self :species) :new) :addAll (send self :map fcn)))
		
(defmethod Collection :select (fcn)
	   (let ((result
		  (mapcan (lambda (x)
				  (when (funcall fcn x)
					(list x)))
			  (send self :asList))))
		(send (send (send self :species) :new) :addAll result)))


; Our final assortment of Collection methods create copies of the object in
; one of several Collection subclasses or as an LISP list.

; :asList will return the collection as a LISP linked list.

(defmethod Collection :asList () (send self :map #'identity))

; :asBag will return the collection as a Bag

(defmethod Collection :asBag ()
	   (let ((result (send Bag :new)))
		(send result :addAll self)
		result))

; :asSet will return the collection as a Set

(defmethod Collection :asSet ()
	   (let ((result (send Set :new)))
		(send result :addAll self)
		result))

; :asArray will return the collection as an Array

(defmethod Collection :asArray ()
	   (send Array :new :initial (send self :asList)))
		

; :asOrderedCollection will return the collection as an OrderedCollection

(defmethod Collection :asOrderedCollection ()
	   (let ((result (send OrderedCollection :new)))
		(send result :addAll self)
		result))


; :asSortedCollection will return the collection as an OrderedCollection

(defmethod Collection :asSortedCollection (&optional (fcn '<))
	   (let ((result (send SortedCollection :new fcn)))
		(send result :addAll self)
		result))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                               ;
;        THE SET CLASS                                          ;
;                                                               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Our first collection will be "Set".  Initialization doesn't have to do
; anything since instance variables are initialized to NIL.	   
; We will use "eql" as the equality test
;

(defclass Set () nil Collection)

(defmethod Set :isnew ())


; We will need :add. But we will ignore the count.

(defmethod Set :add (arg &optional (count 1))
	   (setq data (adjoin arg data))
	   ; Methods typically return (or "answer" in Smalltalk)
	   ; the object, which is bound to "self", if there is
	   ; nothing more appropriate.
	   self)

; We also need to define :remove

(defmethod Set :remove (arg)
	   (let ((pos (position arg data))) ; Find (first) instance
		(when pos ; Delete found element
		      (if (zerop pos)
			  (setq data (cdr data))
			  (setf (cdr (nthcdr (1- pos) data))
				(nthcdr (1+ pos) data))))
		self))

; All the other methods inherited from Collection will work fine

; At last we can test out some collections!

; > (setq x (send Set :new))               Create a new set
; #<An empty Set>

; Note that if your system says "#<An empty SET>" that means you have
; *readtable-case* set to :upcase. It's nothing to be concerned about, but
; if you want the output to match, start over with *readtable-case* set to
; :invert.

; > (send x :add 3)                        Add the element "3"
; #<Set: 3>               
; > (send x :add 1)                        Add the element "1"
; #<Set: 1 3>
; > (send x :add 3)                        Add another 3 -- it's ignored!
; #<Set: 1 3>        
; > (send x :addAll '(1 2 3 4 5))          Add five elements
; #<Set: 5 4 2 1 3>

; We see the order has changed! This doesn't matter because these collections
; are defined to have no order.

; > (send x :remove '3)                    Remove element "3"
; #<Set: 5 4 2 1>                  
; > (send x :select #'evenp)               Create a set with even elements of x
; #<Set: 2 4>
; > (send x :collect #'1+)                 Create a set with incremented
;                                          elements of x
; #<Set: 2 3 5 6>
; > (let ((cnt 0)) (send x :do (lambda (x) (incf cnt x))) cnt)
; 12                                       Summing all the elements in the set

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                               ;
;        THE BAG CLASS                                          ;
;                                                               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


; Our next Collection class will be "Bag" which is an unordered collection
; of objects that we will implement with a hash table. The table value will
; be the number of occurances of the object in the collection.
; It's difficult to calculate the number of elements in a Bag, so we will
; maintain a running total in a new instance variable, size. The defclass
; function will create a :size method for us!
;

; After we've defined this class, we can finally start testing things out.


(defclass Bag (size) nil Collection)

; Because the data in a Bag will be a hash table instead of a list, we
; need to have "isnew" allocate a hash table.
; The entry equality test will be "eql"

(defmethod Bag :isnew nil
	   (setf (send self :data) (make-hash-table)
		 (send self :size) 0))


; We could have done this with "(setf data (make-hash-table) size 0)"
; but this technique is more rigorous.


; The method :add will insert one or more copies of an object in the collection
; We need to adjust the size instance variable when we add objects

(defmethod Bag :add (arg &optional (count 1))
	   (setf (gethash arg data) (+ (gethash arg data 0) count)
		 size (+ size count))
	   self  ; Most methods return Self if there isn't anything else
	   )     ; that is reasonable

; The method :remove will delete an object from the collection
; We need to adjust the size instance variable when we delete objects

(defmethod Bag :remove (arg)
	   (let ((cnt (gethash arg data)))
		(when cnt ; element found
		      (setq size (1- size))
		      (if (= cnt 1)
			  (remhash arg data) ; delete if count would be 0
			  (setf (gethash arg data) (1- cnt))))
		self
		))

; We have to override the definition of :includes since data is stored
; differently in a bag than as a linked list.

(defmethod Bag :includes (arg)
	   (if (gethash arg data) t nil))

; We have to override the definition of :map since data is stored
; differently in a bag than as a linked list.
; Even though :collect is similar, we don't need to redefine it since
; Collection :collect uses :map to do its work.


(defmethod Bag :map (fcn)
	   (if data  ; If in the rare case data isn't set up, we abort
	       (let (result)
		    (maphash (lambda (arg count)
				     (dotimes (i count)
					      (push (funcall fcn arg) result)))
			     data)
		    (nreverse result))
	       nil))


; Now for some Bag examples:


; > (setq y (send Bag :new))                 Create a new bag, y
; #<An empty Bag>
; > (send y :add 3)                          As with set, add 3, 1, 3
; #<Bag: 3>
; > (send y :add 1)
; #<Bag: 3 1>
; > (send y :add 3)
; #<Bag: 3 3 1>                              Now there can be multiple copies!
; > (send y :addAll x)                       Add all the elements of Set x
; #<Bag: 5 4 3 3 2 ...>                      Elipsis means too many to display
; > (send y :asList)                         Use :asList to see entire contents
; (5 4 3 3 2 1 1)
; > (send y :remove 4)
; #<Bag: 5 3 3 2 1 ...>                      Remove still works
; > (send y :select #'oddp)                  Try :select :collect and :do
; #<Bag: 5 3 3 1 1>
; > (send (send y :collect #'1+) :asList)
; (6 4 4 3 2 2)
; > (let ((cnt 0)) (send y :do (lambda (x) (incf cnt x))) cnt)
; 15
; > (send y :asSet)                          Converting a Bag to a Set
; #<Set: 1 2 3 5>                            will delete duplicates


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                               ;
;        THE DICTIONARY CLASS                                   ;
;                                                               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


; The Dictionary class will be implemented using a hash table like a Bag.
; The hash table will use #'equal for comparisons, allowing string keys
;

(defclass Dictionary () nil Collection)

(defmethod Dictionary :isnew nil
	   (setf (send self :data) (make-hash-table :test #'equal)))

; Getting the size of a Dictionary is slightly different than the default
	   
(defmethod Dictionary :size () (hash-table-count data))

; We need to define the :at and :atPut methods. :at will be extended
; to allow a keyword argument "ifAbsent" to supply the return value.
; It's a closure, just like in Smalltalk

(setq gened (gensym))  ; We need a unique symbol

(defmethod Dictionary :at (key &key ifAbsent)
	   (let ((value (gethash key data '#.gened)))
		(if (eq value '#.gened)
		    (if ifAbsent
			(funcall ifAbsent)
			nil)
		    value)))


(defmethod Dictionary :atPut (key value)
	   (setf (gethash key data) value)
	   self)


; :addAll needs to be redefined, and requires a list of key-value pairs.
; This method makes :storeon much simpler.

(defmethod Dictionary :addAll (arg)
	   (if (or (listp arg) (arrayp arg) (stringp arg))
	       ; Use map when argument is a sequence
	       (map nil
		    (lambda (x) (send self :atPut (first x) (second x)))
		    arg)
	       ; Otherwise, send :map to the argument collection
	       (send arg
		     :map
		     (lambda (x)
			     (send self :atPut (first x) (second x)))))
	   self)



; :remove won't work for a Dictionary, since we want to remove key/value
; associations. Thus we have :removeKey, with an optional ifAbsent.

(defmethod Dictionary :removeKey (key &key ifAbsent)
	   (if (eq (gethash key data '#.gened) '#.gened)
	       (progn
		(remhash key data)
		(setq count (1- count)))
	       (when ifAbsent (funcall ifabsent)))
	   self)

(unintern gened) ; We don't need this symbol anymore

; :keys returns a set of the keys

(defmethod Dictionary :keys ()
	   (let (list)
		(maphash (lambda (key value) (setq list (cons key list)))
			 data)
		(send (send Set :new) :addAll list)))

; :values returns a bag of the values

(defmethod Dictionary :values ()
	   (let (list)
		(maphash (lambda (key value) (setq list (cons value list)))
			 data)
	   (send (send Bag :new) :addAll list)))

; :map is defined to work over the values

(defmethod Dictionary :map (fcn)
	   (let (list)
		(maphash (lambda (key value)
				 (setq list (cons (funcall fcn value) list)))
			  data)
		list))

; We have to override the definition of :includes since data is stored
; differently in a Dictionary than as a linked list.

(defmethod Dictionary :includes (arg)
	   (if (position arg (send self :asList)) t nil))

; :collect, :select aren't appropriate

(defmethod Dictionary :collect (arg) 
	   (send self :notImplemented :collect))

(defmethod Dictionary :select (arg) 
	   (send self :notImplemented :select))

; :prin1 needs to be overridden to show both keys and data

(defmethod Dictionary :prin1 (&optional (stream *standard-output*))
	   (let (contents ; get collection as a list
			  ; and get our class' name
			  ; (it might not be "Dictionary")
	         (cls (send (send self :class) :pname))) 
		(maphash (lambda (x y)
				 (setq contents (cons (list x y) contents)))
			 data)
		(cond ((null contents)
		       (format stream
			       "#<An empty ~a>" cls))
		      ((< (length contents) 6)
		       (format stream
			       "#<~a:~{ ~s~}>" cls contents))
		      (t
		       (format stream
			       "#<~a:~5{ ~s~} ...>" cls contents)))))


; A different :storeon is needed as well

(defmethod Dictionary :storeon ()
	   (let (contents) ; get collection as a list
		(maphash (lambda (x y)
				 (setq contents (cons (list x y) contents)))
			 data)
		(list 'send
		      (list 'send 'Dictionary :new)
		 :addAll
		 (list
		  'quote
		  contents))))

; Class Dictionary examples

; > (setq z (send Dictionary :new))              Create a new dictionary
; #<An empty Dictionary>
; > (send z :addAll '((a 1) (b 2) (c 3) (d 4)))  Quickly add 4 entries
; #<Dictionary: (a 1) (b 2) (c 3) (d 4)>
; > (send z :at 'b)                              Given a key, returns value
; 2
; > (send z :at 'e :ifAbsent (lambda () "Key Not Found")) Check ":ifAbsent"
; "Key Not Found"
; > (send z :atPut 'b 7)                         :atPut will change value
; #<Dictionary: (a 1) (b 7) (c 3) (d 4)>
; > (send z :atPut 'e 100)                       :atPut will create new entries
; #<Dictionary: (a 1) (b 7) (c 3) (d 4) (e 100)>
; > (send z :asBag)                              Converting to Bag just gives
; #<Bag: 7 100 4 3 1>                            values



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                               ;
;        THE SEQUENCEABLECOLLECTION CLASS                       ;
;                                                               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


; The class SequenceableCollection is, like Collection, an abstract class.
; This is a good thing since who would want to type "SequenceableCollection"
; very often?
;

(defclass SequenceableCollection () nil Collection)

; Some methods can be defined that will work for all subclasses of
; SequenceableCollection. The minimum index value is 0.

(defmethod SequenceableCollection :at (arg) (elt data arg))

(defmethod SequenceableCollection :atPut (arg value)
	   (setf (elt data arg) value)
	   self)

(defmethod SequenceableCollection :first () (self :at 0))

(defmethod SequenceableCollection :last () (self :at (1- (send self :size))))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                               ;
;        THE ARRAY CLASS                                        ;
;                                                               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


; The Array class -- implemented using an array.
; Because it has a fixed size, we have to allocate space for it when
; we create it. We will allow for initialization, since :addAll won't work.
; Either : (send Array :new :size 10) for example, to create an array of 10
; entries or (send Array :new :initial (1 2 3 4 5)) for an initialized array.


(defclass Array () nil SequenceableCollection)

(defmethod Array :isnew (&key size initial)
	   ; Size must be specified when creating array
	   (if size
	       (setf (send self :data) (make-array size))
	       (setf (send self :data)
		     (make-array (length initial) :initial-contents initial))))

; We have to override :collect because (send Array :new) won't work.
; But we can optimize while we are at it.

(defmethod Array :collect (fcn)
	   (let ((result (send Array :new :size (send self :size))))
		(map-into (send result :data) fcn data)
		result))

; We also have to override :select, for the same reason

(defmethod Array :select (fcn)
	   (let ((result
		  (mapcan (lambda (x)
				  (when (funcall fcn x)
					(list x)))
			  (coerce (send self :data) 'list))))
		(send (send self :class) :new :initial result)))

; Finally, :storeon needs to be changed since :addAll doesn't work for
; arrays.	   

(defmethod Array :storeon ()
	   (list 'send
		 'Array
		 :new
		 :initial
		 (list
		  'quote
		  (send self :asList))))


; Test of the Array class:

; > (setq a (send x :asArray))              Make Array from Set x
; #<Array: 5 4 2 1>
; > (send a :atPut 1 10)                    Change an element
; #<Array: 5 10 2 1>
; > (send a :select #'evenp)                Get array of even elements
; #<Array: 10 2>
; > (send a :collect #'1+)                  Make array with values 1 larger
; #<Array: 6 11 3 2>             


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                               ;
;        THE ORDEREDCOLLECTION CLASS                            ;
;                                                               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


; The OrderedCollection class uses linked lists and doesn't have the
; allocation problems of Array.
; Adding or deleteing from the start alters the index numbers,
; so we need a new instance variable to hold the offset.


(defclass OrderedCollection (offset) nil SequenceableCollection)

(defmethod OrderedCollection :isnew (&optional (offset 0))
	   ; Optional argument sets offset of first element.
	   ; This is a "private" feature to aid storeon.
	   (setf (send self :offset) offset))

; :at, :atPut, :first, and :last need revision

(defmethod OrderedCollection :at (arg) (elt data (+ arg offset)))

(defmethod OrderedCollection :atPut (arg value)
	   (setf (elt data (+ arg offset)) value)
	   self)

(defmethod OrderedCollection :first () (car data))

(defmethod OrderedCollection :last () (car (last data)))

; We need to implement add and remove for both ends
; :add will be equivalent to :addLast

(defmethod OrderedCollection :add (arg)
	   (setq data (nconc data (list arg)))
	   self)

(defmethod OrderedCollection :addlast (arg) (send self :add arg))

(defmethod OrderedCollection :addFirst (arg)
	   (setq offset (1+ offset))
	   (setq data (cons arg data))
	   self)

(defmethod OrderedCollection :removeFirst ()
	   (unless (zerop (length data))
		   (setq offset (1- offset))
		   (prog1 (car data) (setq data (cdr data)))))

(defmethod OrderedCollection :removeLast ()
	   (prog1 (car (last data)) (setq data (nbutlast data))))


; Finally, storeon is modified so that offset will be set


(defmethod OrderedCollection :storeon ()
	   (list 'send
		 (if (zerop offset)
		     (list 'send
			   (intern (send (send self :class) :pname))
			   :new)
		     (list 'send
			   (intern (send (send self :class) :pname))
			   :new
			   offset))
		 :addAll
		 (list
		  'quote
		  (send self :asList))))


; Example of use of OrderedCollection:


; > (setq  c (send a :asOrderedCollection))   Make one from Array a
; #<OrderedCollection: 5 10 2 1>
; > (send c :at 1)                            Value at index 1 is 10
; 10
; > (send c :addFirst 7)                      Add to front of collection
; #<OrderedCollection: 7 5 10 2 1>
; > (send c :at 1)                            Index 1 is same spot
; 10
; > (send c :removeLast)                      Remove from either end
; 1
; > (send c :last)                            Last element is now 2
; 2
; > (send c :asArray)                         Convert back to an array
; #<Array: 7 5 10 2>                        



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                               ;
;        THE SORTEDCOLLECTION CLASS                             ;
;                                                               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


; The SortedCollection class requires a sort function. The collection gets
; re-sorted whenever a new element is added. This is a subclass of
; OrderedCollection.

; Offset won't change for this class.	   


(defclass SortedCollection (sortfcn) nil OrderedCollection)

(defmethod SortedCollection :isnew (&optional (fcn '< ))	
	   (setq sortfcn fcn)
	   (send-super :isnew))

(defmethod SortedCollection :selfSort ()
	   ; "private" method that sorts the list
	   (setq data (sort data sortfcn))
	   self)

(defmethod SortedCollection :add (arg)
	   (send-super :add arg)
	   (send self :selfSort))

; Don't allow addFirst, addLast, removefirst, removelast, or atPut

(defmethod SortedCollection :addFirst (arg) 
	   (send self :notImplemented :addFirst))

(defmethod SortedCollection :addLast (arg) 
	   (send self :notImplemented :addLast))

(defmethod SortedCollection :removeFirst (arg) 
	   (send self :notImplemented :removeFirst))

(defmethod SortedCollection :removeLast (arg) 
	   (send self :notImplemented :removeLast))

(defmethod SortedCollection :atPut (arg1 arg2)
	   (send self :notImplemented :atPut))

; We need a way to remove elements from a Sorted Collection.
; :remove (specifying the element) will do just fine.

(defmethod SortedCollection :remove (arg)
	   (let ((pos (position arg data))) ; Find (first) instance
		(when pos ; Delete found element
		      (if (zerop pos)
			  (setq data (cdr data))
			  (setf (cdr (nthcdr (1- pos) data))
				(nthcdr (1+ pos) data))))
		self))


; Finally, storeon is modified so that the sort function will be set


(defmethod SortedCollection :storeon ()
	   (list 'send
		 (list 'send
		       (intern (send (send self :class) :pname))
		       :new
		       (list
			'quote
			sortfcn))
		 :addAll
		 (list
		  'quote
		  (send self :asList))))

; Let's see how the SortedCollection works:

; > (setq s (send c :asSortedCollection))    Sorted when it is created
; #<SortedCollection: 2 5 7 10>
; > (send s :add 8)                          :add puts new element in order
; #<SortedCollection: 2 5 7 8 10>
; > (send s :asSortedCollection #'>)         New collection with order reversed
; #<SortedCollection: 10 8 7 5 2>
; > (send (send (send Set :new) :addAll '(5 3 8 2 5 4 8)) :asSortedCollection)
; #<SortedCollection: 2 3 4 5 8>    Eliminate duplicates and sort
; > (send * :asList)
; (2 3 4 5 8)        


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                               ;
;        THE INTERVAL CLASS                                     ;
;                                                               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



; The Interval class is considerably different than the others in that it has
; no "data" but calculates each elements value. We'll use three new
; instance variables -- start end and step. The :new function will take two
; or three arguments to specify start, end and step, with the step size
; defaulting to 1 not defined. We will set the data instance
; variable to T if the range is valid, and to NIL if not (no elements)


(defclass Interval (start end step) nil SequenceableCollection)

(defmethod Interval :isnew (arg1 arg2 &optional arg3)
	   (if arg3
	       (setq data (or (and (<= arg1 arg2)(> arg3 0))
			      (and (>= arg1 arg2)(< arg3 0))))
	       (setq arg3 1 
		     data (<= arg1 arg2)))
	   (setq start arg1 end arg2 step arg3)
	   ; Correct End value if necessary
	   (unless (zerop (rem (- end start) step))
		   (setq end (- end (rem (- end start) step))))
	   )

; :at calculates value. We won't check for out of range.

(defmethod Interval :at (arg) (+ start (* step arg)))

; :atPut isn't allowed

(defmethod Interval :atPut (arg1 arg2) (send self :notImplemented :atPut))

; :size returns calculated size

(defmethod Interval :size () (if data (1+ (truncate (- end start) step)) 0))

; :includes must be calcuated

(defmethod Interval :includes (arg)
	   (cond
	    ((null data) nil)
	    ((> step 0) (and (>= arg start)
			     (<= arg end)
			     (zerop (rem (- arg start) step))))
	    (t          (and (<= arg start)
			     (>= arg end)
			     (zerop (rem (- arg start) step))))))

; While Collection bases :asList on :map, we want to base :map on
; :asList

(defmethod Interval :map (fcn) (mapcar fcn (send self :asList)))

(defmethod Interval :asList ()
	   (let ((result nil))
		(when data
		      (dotimes (i (send self :size))
			       (setq result (cons (+ start (* i step))
						  result))))
		(nreverse result)))

; Since :do is used often with an Interval, and since the default method
; would create a list of values, it would make sense to reimplement :do
; here as an Interval method. That will be left as an exercise for the
; reader!

; :collect, :select will work because we will redefine :species to
; create an OrderedCollection rather than an Interval

(defmethod Interval :species () OrderedCollection)

; Override printing methods 

(defmethod Interval :prin1 (&optional (stream *standard-output*))
	   (format stream
		   "#<~a from ~s to ~s by ~s>"
		   (send (send self :class) :pname)
		   start end step))

; Override :storeon -- this one becomes really easy

(defmethod Interval :storeon ()
	   (list 'send 'Interval :new start end step))


; A few examples of the use of the Interval class:

; > (setq i (send Interval :new 2 10 2))        Make an interval, i
; #<Interval from 2 to 10 by 2>
; > (send i :do (lambda (x) (format t "~s " x)))  Demonstrate :do
; 2 4 6 8 10
; nil
; > (send i :at 3)                              Check operation of :at
; 8                  
; > (send i :size)                              Size of interval
; 5
; > (send i :asList)                            Convert to a list
; (2 4 6 8 10)
; > (send i :asSortedCollection #'>)            Convert to a SortedCollection
; #<SortedCollection: 10 8 6 4 2>               sequence changes!

(in-package "USER")       ; revert to default package
(use-package "CLASSES")    ; Make the classes package accessable