git.fiddlerwoaroof.com
Browse code

Add new features !

fiddlerwoaroof authored on 22/12/2016 17:09:01
Showing 8 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,6 @@
1
+(in-package #:fwoar.lisputils)
2
+
3
+(defmacro with-accessors* ((&rest accessors) object &body body)
4
+  `(with-accessors ,(ensure-mapping accessors) ,object
5
+     ,@body))
6
+       
0 7
new file mode 100644
... ...
@@ -0,0 +1,55 @@
1
+(defpackage :fwoar.counter
2
+  (:use :cl))
3
+
4
+(in-package :fwoar.counter)
5
+
6
+(defclass counter ()
7
+  ((%counts :type hash-table :accessor item-counts)
8
+   (%test :initarg :test :initform 'eql :accessor counter-test)
9
+   (%key :initarg :key :initform 'identity :accessor counter-key)))
10
+
11
+(defmethod initialize-instance :after ((counter counter) &rest initargs &key test)
12
+  (declare (ignorable initargs))
13
+  (setf (item-counts counter) (make-hash-table :test test)))
14
+
15
+(defgeneric count-sequence (sequence &key test key)
16
+  (:documentation "take a sequence, count it using test to compare elements and key to extract values from them"))
17
+
18
+(defgeneric update-counts (counter sequence)
19
+  (:documentation "given a sequence, update a counter"))
20
+
21
+(defgeneric extract-count (counter item)
22
+  (:documentation "Given a counter and an item, return the number of times that item has been counted."))
23
+
24
+(defgeneric format-counts (counter stream)
25
+  (:documentation "Given a counter and a stream, format the counter's counts to that stream"))
26
+
27
+(defmethod extract-count ((counter counter) item)
28
+  (gethash (funcall (counter-key counter)
29
+                    item)
30
+           (item-counts counter)
31
+           0))
32
+
33
+(defmethod count-sequence ((sequence string) &key (test 'eql) (key 'identity))
34
+  (let ((result (make-instance 'counter :test test :key key)))
35
+    (loop for c across sequence
36
+          do (incf (gethash (funcall key c)
37
+                            (item-counts result)
38
+                            0))
39
+          finally (return result))))
40
+
41
+(defmethod format-counts ((counter counter) (stream stream))
42
+  (let ((result '()))
43
+    (maphash (lambda (key val)
44
+               (push (list key val)
45
+                     result))
46
+             (item-counts counter))
47
+    (format t "~:{~s: ~2d~%~}"
48
+            (stable-sort result #'< :key #'cadr))))
49
+
50
+(defmethod update-counts ((counter counter) sequence)
51
+  (with-accessors ((item-counts item-counts) (test counter-test) (key counter-key)) counter
52
+    (maphash (lambda (key value)
53
+               (incf (gethash key item-counts 0)
54
+                     value))
55
+             (item-counts (count-sequence sequence :test test :key key)))))
... ...
@@ -6,6 +6,16 @@
6 6
   :license "MIT"
7 7
   :serial t
8 8
   :components ((:file "package")
9
-               (:file "fwoar.lisputils"))
10
-  :depends-on (#:anaphora #:alexandria #:iterate #:plump))
9
+               (:file "fwoar.lisputils")
10
+	       (:file "hash-functions")
11
+	       (:file "multiple-values")
12
+	       (:file "clos-helpers")
13
+	       (:file "vector-utils"))
14
+  :depends-on (#:anaphora
15
+	       #:alexandria
16
+	       #:serapeum
17
+	       #:cl-containers
18
+	       #:iterate
19
+	       #:plump
20
+	       #:positional-lambda))
11 21
 
... ...
@@ -48,55 +48,96 @@
48 48
      ,@body))
49 49
 
50 50
 (eval-when (:compile-toplevel :load-toplevel :execute)
51
+  (defun map-cons (cb cons)
52
+    (cond
53
+      ((null cons) '())
54
+      ((consp (cdr cons)) (cons (funcall cb (car cons))
55
+				(map-cons cb (cdr cons))))
56
+      (t (list (funcall cb (car cons))
57
+	       (funcall cb (cdr cons))))))
58
+
59
+  (defun generate-declarations-for (sym ignored ignorable)
60
+    (let ((ignores (list))
61
+	  (ignorables (list)))
62
+      (map-cons (op (cond ((member _1 ignorable)
63
+			   (push _1 ignorables))
64
+			  ((member _1 ignored)
65
+			   (push _1 ignores))))
66
+		(ensure-cons sym))
67
+      (if (or ignores ignorables)
68
+	  `((declare
69
+	     ,@(when ignores
70
+		 `((ignore ,@ignores)))
71
+	     ,@(when ignorables
72
+		 `((ignorable ,@ignorables)))))
73
+	  '())))
74
+
75
+  (defun find-ignored-vars (body)
76
+    (let ((possible-declarations (car body))
77
+	  (ignored-vars nil)
78
+	  (ignorable-vars nil))
79
+      (if (and (eq (car possible-declarations) 'declare)
80
+	       (consp (cadr possible-declarations)))
81
+	  (let* ((declarations (cdr possible-declarations)))
82
+	    (setf ignored-vars (cdr (assoc 'ignore declarations))
83
+		  ignorable-vars (cdr (assoc 'ignorable declarations))
84
+		  body (cdr body))))
85
+      (values ignored-vars
86
+	      ignorable-vars
87
+	      body)))
88
+
51 89
   (defun ensure-mapping (list)
52 90
     "Take a list and make sure that it's parseable as a let-style binding.
53 91
      Very handy for certain sorts of macros."
54 92
     (let ((symbols->mappings (lambda-cond (x)
55
-                               ((symbolp x) `(,x ,x))
56
-                               ((null (cdr x)) `(,#1=(car x) ,#1#))
57
-                               (t x))))
93
+					  ((symbolp x) `(,x ,x))
94
+					  ((null (cdr x)) `(,#1=(car x) ,#1#))
95
+					  (t x))))
58 96
       (mapcar symbols->mappings list)))
59 97
 
60 98
 
61
-    (defun rollup-list (list &optional body)
62
-      (labels ((helper (list &optional accum start)
63
-                 (tagbody
64
-                   start
65
-                   (cond
66
-                     ((endp list) (return-from rollup-list accum))
67
-                     (t (psetf accum  (cond
68
-                                        ((null accum) (car list))
69
-                                        (start `(,@(car list) ,@accum)) 
70
-                                        (t `(,@(car list) ,accum)))
71
-                               list (cdr list)
72
-                               start nil)
73
-                        ;; NOTE: REMEMBER! This call to #'GO is the "tail call"
74
-                        (go start))))))
75
-        (helper (reverse list) body t))))
99
+  (defun rollup-list (list &optional body)
100
+    (labels ((helper (list &optional accum start)
101
+	       (tagbody
102
+		start
103
+		  (cond
104
+		    ((endp list) (return-from rollup-list accum))
105
+		    (t (psetf accum  (cond
106
+				       ((null accum) (car list))
107
+				       (start `(,@(car list) ,@accum)) 
108
+				       (t `(,@(car list) ,accum)))
109
+			      list (cdr list)
110
+			      start nil)
111
+		       ;; NOTE: REMEMBER! This call to #'GO is the "tail call"
112
+		       (go start))))))
113
+      (helper (reverse list) body t))))
76 114
 
77 115
 (defmacro m-lambda (sym &rest args)
78 116
   (let ((arglist (loop for x in args
79
-                       unless (member x (list '&optional '&key '&rest))
80
-                       collect (ctypecase x
81
-                                          (cons                  (car x))
82
-                                          ((or symbol keyword string) x)))))
117
+		       unless (member x (list '&optional '&key '&rest))
118
+		       collect (ctypecase x
119
+					  (cons                  (car x))
120
+					  ((or symbol keyword string) x)))))
83 121
     `(lambda (,@args)
84 122
        (,sym ,@arglist))))
85 123
 
86 124
 (defmacro destructuring-lambda ((&rest args) &body body)
87 125
   "A lambda whose arguments can be lambda-lists to be destructured"
88
-  (let* ((args-syms (mapcar (alambda (gensym "arg"))
89
-                            args))
90
-         (args (mapcar #'list args args-syms))
91
-         (destructuring-expressions
92
-           (rollup-list
93
-               (loop for (arg arg-sym) in args
94
-                     collect (if (consp arg)
95
-                               `(destructuring-bind ,arg ,arg-sym)
96
-                               `(let ((,arg ,arg-sym)))))
97
-               body)))
98
-    `(lambda ,args-syms
99
-       ,destructuring-expressions)))
126
+  (multiple-value-bind (ignored ignorable body) (find-ignored-vars body)
127
+    (let* ((args-syms (mapcar (lambda (_) (declare (ignore _)) (gensym "arg"))
128
+			      args))
129
+	   (args (mapcar #'list args args-syms))
130
+	   (destructuring-expressions
131
+	    (rollup-list
132
+	     (loop for (arg arg-sym) in args
133
+		collect (if (consp arg)
134
+			    `(destructuring-bind ,arg ,arg-sym
135
+			       ,@(generate-declarations-for arg ignored ignorable))
136
+			    `(let ((,arg ,arg-sym))
137
+			       ,@(generate-declarations-for arg ignored ignorable))))
138
+	     body)))
139
+      `(lambda ,args-syms
140
+	 ,destructuring-expressions))))
100 141
 
101 142
 
102 143
 ;;; CASES:::
... ...
@@ -190,9 +231,37 @@
190 231
   "Make setf a bit nicer to use with paredit"
191 232
   (list* 'setf (apply #'append body)))
192 233
 
234
+(defmacro prog2-let (first-form (&rest result-binding) &body body)
235
+  "Execute a form, make a bunch of bindings and retern the bound values via prog1 after executing body"
236
+  `(progn ,first-form
237
+	  (let (,@result-binding)
238
+	    (prog1 (list ,@(mapcar #'car result-binding))
239
+	      ,@body))))
240
+
193 241
 (defmacro prog1-let ((&rest result-binding) &body body)
242
+  "Bind a bunch of symbols to values and return them via prog1"
194 243
   `(let (,@result-binding)
195 244
      (prog1 (list ,@(mapcar #'car result-binding))
196 245
        ,@body)))
197 246
 
247
+(defmacro if-let* ((&rest bindings) &body (then-form &optional else-form))
248
+  "Like if-let, but sets bindings sequentially.  Doesn't short-circuit."
249
+  `(let* ,bindings
250
+     (if (and ,@(mapcar #'car bindings))
251
+       ,then-form
252
+       ,else-form)))
253
+
254
+(defmacro with ((var val) &body body)
255
+  "A stripped down let for binding a single name"
256
+  `(let ((,var ,val))
257
+     ,@body))
258
+
259
+(define-modify-macro aconsf (key datum)
260
+  (lambda (alist key datum)
261
+    (acons key datum alist)))
262
+
263
+;(defun ensure-list (val)
264
+;  (typecase val
265
+;    (list val)
266
+;    (t (list val))))
198 267
 
199 268
new file mode 100644
... ...
@@ -0,0 +1,6 @@
1
+(in-package #:fwoar.lisputils)
2
+
3
+(defun pick (keys h-t)
4
+  (mapcar (plambda:plambda (gethash :1 h-t))
5
+          keys))
6
+
0 7
new file mode 100644
... ...
@@ -0,0 +1,31 @@
1
+(in-package :fwoar.lisputils)
2
+
3
+;; (multiple-value-call #'local-time:encode-timestamp
4
+;;   (limit-values 7
5
+;;                 (substitute-values 0 0 0 1
6
+;;                                    (local-time:decode-timestamp
7
+;;                                      (local-time:now)))))
8
+
9
+(defmacro skip-values (n form)
10
+  (let* ((gensyms (loop repeat n collect (gensym "V")))
11
+	 (ignore-sym (gensym))
12
+	 (value-limiter `(lambda (,@gensyms &rest ,ignore-sym)
13
+			   (declare (ignore ,@gensyms))
14
+			   (values-list ,ignore-sym))))
15
+    `(multiple-value-call ,value-limiter ,form)))
16
+
17
+(defmacro limit-values (n form)
18
+  (let* ((gensyms (loop repeat n collect (gensym "V")))
19
+	 (ignore-sym (gensym))
20
+	 (value-limiter `(lambda (,@gensyms &rest ,ignore-sym)
21
+			   (declare (ignore ,ignore-sym))
22
+			   (values ,@gensyms))))
23
+    `(multiple-value-call ,value-limiter ,form)))
24
+
25
+(defmacro substitute-values (&rest forms)
26
+  (let* ((call (car (last forms)))
27
+	 (values (butlast forms))
28
+	 (num-values (length values)))
29
+    `(multiple-value-call #'values
30
+       ,@values
31
+       (skip-values ,num-values ,call))))
... ...
@@ -3,6 +3,7 @@
3 3
 (defpackage #:fwoar.lisputils
4 4
   (:use #:cl #:alexandria #:iterate)
5 5
   (:nicknames #:fw.lu)
6
+  (:import-from #:serapeum #:op)
6 7
   (:export #:lambda-if #:lambda-cond #:alambda #:rollup-list
7 8
            #:ensure-mapping #:alist-string-hash-table #:make-pairs
8 9
            #:copy-slots #:transform-alist #:%json-pair-transform
... ...
@@ -10,5 +11,8 @@
10 11
            #:transform-result #:slots-to-pairs #:normalize-html
11 12
            #:destructuring-lambda #:let-each #:let-first #:let-second
12 13
            #:neither #:neither-null #:m-lambda #:sets #:defparameters
13
-           #:setfs #:prog1-let))
14
+           #:setfs #:prog1-let #:if-let* #:with #:aconsf #:ensure-list #:pick
15
+	   #:vector-destructuring-bind #:with-accessors*
16
+	   #:skip-values #:limit-values #:substitute-values
17
+	   #:op))
14 18
 
15 19
new file mode 100644
... ...
@@ -0,0 +1,42 @@
1
+(in-package #:fwoar.lisputils)
2
+
3
+(defmacro vector-destructuring-bind ((&rest symbols) vector &body body)
4
+  (let ((mappings (loop for symbol in symbols
5
+		     for num from 0
6
+		     collect (list num symbol))))
7
+    (once-only (vector)
8
+      `(symbol-macrolet ,(mapcar (destructuring-lambda ((num symbol))
9
+				   `(,symbol (aref ,vector ,num)))
10
+				 mappings)
11
+	 ,@body))))
12
+
13
+(defun v-first (vector)
14
+  (elt vector 0))
15
+
16
+(defun index-table (table &key (test 'eql) (key 'v-first))
17
+  (declare (optimize (speed 0) (debug 3)))
18
+  (check-type table (array * (* *)))
19
+  (let ((table-index (cl-containers:make-container 'cl-containers:simple-associative-container :test test)))
20
+    (loop for row-num from 0 to (1- (array-dimension table 0))
21
+       for current-row = (make-array (array-dimension table 1)
22
+				     :displaced-to table
23
+				     :displaced-index-offset (apply #'array-row-major-index table (list row-num 0)))
24
+       do (setf (cl-containers:item-at table-index (funcall key current-row)) row-num))
25
+    table-index))
26
+
27
+(defun join-tables (table1 table2 &key (test 'eql) (key1 'v-first) (key2 'v-first))
28
+  (declare (optimize (speed 0) (debug 3)))
29
+  (check-type table1 (array * (* *)))
30
+  (check-type table2 (array * (* *)))
31
+  (let ((table2-index (index-table table2 :test test :key key2)))
32
+    (loop for row-num from 0 to (1- (array-dimension table1 0))
33
+       for current-row = (make-array (array-dimension table1 1)
34
+				     :displaced-to table1
35
+				     :displaced-index-offset (apply #'array-row-major-index table1 (list row-num 0)))
36
+       for dest-index = (cl-containers:item-at table2-index (funcall key1 current-row))
37
+       when dest-index
38
+       collect (let* ((to-row (make-array (array-dimension table2 1)
39
+					  :displaced-to table2
40
+					  :displaced-index-offset (apply #'array-row-major-index table2 (list dest-index 0)))))
41
+		 (concatenate 'vector current-row to-row)))))
42
+