/racket-5-0-2-bin-i386-osx-mac-dmg/collects/srfi/1/lset.rkt

http://github.com/smorin/f4f.arc · Racket · 227 lines · 146 code · 26 blank · 55 comment · 24 complexity · fdb323b390e415f606e943297573da90 MD5 · raw file

  1. ;;;
  2. ;;; <lset.ss> ---- Lists as Sets
  3. ;;; Time-stamp: <03/03/13 16:20:56 noel>
  4. ;;;
  5. ;;; Copyright (C) 2002 by Noel Welsh.
  6. ;;;
  7. ;;; This file is part of SRFI-1.
  8. ;;; SRFI-1 is free software; you can redistribute it and/or
  9. ;;; modify it under the terms of the GNU Lesser General Public
  10. ;;; License as published by the Free Software Foundation; either
  11. ;;; version 2.1 of the License, or (at your option) any later version.
  12. ;;; SRFI-1 is distributed in the hope that it will be useful,
  13. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. ;;; Lesser General Public License for more details.
  16. ;;; You should have received a copy of the GNU Lesser General Public
  17. ;;; License along with SRFI-1; if not, write to the Free Software
  18. ;;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19. ;;; Author: Noel Welsh <noelwelsh@yahoo.com>
  20. ;;
  21. ;;
  22. ;; Commentary:
  23. ;; Based on the reference implementation by Olin Shiver and hence:
  24. ;; Copyright (c) 1998, 1999 by Olin Shivers. You may do as you please with
  25. ;; this code as long as you do not remove this copyright notice or
  26. ;; hold me liable for its use. Please send bug reports to shivers@ai.mit.edu.
  27. ;; -Olin
  28. #lang scheme/base
  29. (require srfi/optional
  30. (rename-in "search.ss" [member s:member])
  31. (except-in "fold.ss" map for-each)
  32. "delete.ss"
  33. "predicate.ss"
  34. (only-in "filter.ss" [filter-with-sharing s:filter] partition))
  35. (provide lset<=
  36. lset=
  37. lset-adjoin
  38. lset-union (rename-out [lset-union lset-union!])
  39. lset-intersection
  40. lset-difference (rename-out [lset-difference lset-difference!])
  41. lset-xor (rename-out [lset-xor lset-xor!])
  42. lset-diff+intersection
  43. (rename-out [lset-diff+intersection lset-diff+intersection!]))
  44. ;; Lists-as-sets
  45. ;;;;;;;;;;;;;;;;;
  46. ;; This is carefully tuned code; do not modify casually.
  47. ;; - It is careful to share storage when possible;
  48. ;; - Side-effecting code tries not to perform redundant writes.
  49. ;; - It tries to avoid linear-time scans in special cases where constant-time
  50. ;; computations can be performed.
  51. ;; - It relies on similar properties from the other list-lib procs it calls.
  52. ;; For example, it uses the fact that the implementations of MEMBER and
  53. ;; FILTER in this source code share longest common tails between args
  54. ;; and results to get structure sharing in the lset procedures.
  55. (define (%lset2<= = lis1 lis2) (every (lambda (x) (s:member x lis2 =)) lis1))
  56. (define (lset<= = . lists)
  57. (check-arg procedure? = 'lset<=)
  58. (or (not (pair? lists)) ; 0-ary case
  59. (let lp ((s1 (car lists)) (rest (cdr lists)))
  60. (or (not (pair? rest))
  61. (let ((s2 (car rest)) (rest (cdr rest)))
  62. (and (or (eq? s2 s1) ; Fast path
  63. (%lset2<= = s1 s2)) ; Real test
  64. (lp s2 rest)))))))
  65. (define (lset= = . lists)
  66. (check-arg procedure? = 'lset=)
  67. (or (not (pair? lists)) ; 0-ary case
  68. (let lp ((s1 (car lists)) (rest (cdr lists)))
  69. (or (not (pair? rest))
  70. (let ((s2 (car rest))
  71. (rest (cdr rest)))
  72. (and (or (eq? s1 s2) ; Fast path
  73. (and (%lset2<= = s1 s2) (%lset2<= = s2 s1))) ; Real test
  74. (lp s2 rest)))))))
  75. (define (lset-adjoin = lis . elts)
  76. (check-arg procedure? = 'lset-adjoin)
  77. (fold (lambda (elt ans) (if (s:member elt ans =) ans (cons elt ans)))
  78. lis elts))
  79. (define (lset-union = . lists)
  80. (check-arg procedure? = 'lset-union)
  81. (reduce (lambda (lis ans) ; Compute ANS + LIS.
  82. (cond ((null? lis) ans) ; Don't copy any lists
  83. ((null? ans) lis) ; if we don't have to.
  84. ((eq? lis ans) ans)
  85. (else
  86. (fold (lambda (elt ans)
  87. (if (any (lambda (x) (= x elt)) ans)
  88. ans
  89. (cons elt ans)))
  90. ans lis))))
  91. '() lists))
  92. #; ; lists are immutable
  93. (define (lset-union! = . lists)
  94. (check-arg procedure? = 'lset-union!)
  95. (reduce (lambda (lis ans) ; Splice new elts of LIS onto the front of ANS.
  96. (cond ((null? lis) ans) ; Don't copy any lists
  97. ((null? ans) lis) ; if we don't have to.
  98. ((eq? lis ans) ans)
  99. (else
  100. (pair-fold (lambda (pair ans)
  101. (let ((elt (car pair)))
  102. (if (any (lambda (x) (= x elt)) ans)
  103. ans
  104. (begin (set-cdr! pair ans) pair))))
  105. ans lis))))
  106. '() lists))
  107. (define (lset-intersection = lis1 . lists)
  108. (check-arg procedure? = 'lset-intersection)
  109. (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.
  110. (cond ((any null-list? lists) '()) ; Short cut
  111. ((null? lists) lis1) ; Short cut
  112. (else (filter (lambda (x)
  113. (every (lambda (lis) (s:member x lis =)) lists))
  114. lis1)))))
  115. #; ; lists are immutable
  116. (define (lset-intersection! = lis1 . lists)
  117. (check-arg procedure? = 'lset-intersection!)
  118. (let ((lists (delete lis1 lists eq?))) ; Throw out any LIS1 vals.
  119. (cond ((any null-list? lists) '()) ; Short cut
  120. ((null? lists) lis1) ; Short cut
  121. (else (filter! (lambda (x)
  122. (every (lambda (lis) (s:member x lis =)) lists))
  123. lis1)))))
  124. (define (lset-difference = lis1 . lists)
  125. (check-arg procedure? = 'lset-difference)
  126. (let ((lists (filter pair? lists))) ; Throw out empty lists.
  127. (cond ((null? lists) lis1) ; Short cut
  128. ((memq lis1 lists) '()) ; Short cut
  129. (else (filter (lambda (x)
  130. (every (lambda (lis) (not (s:member x lis =)))
  131. lists))
  132. lis1)))))
  133. #; ; lists are immutable
  134. (define (lset-difference! = lis1 . lists)
  135. (check-arg procedure? = 'lset-difference!)
  136. (let ((lists (filter pair? lists))) ; Throw out empty lists.
  137. (cond ((null? lists) lis1) ; Short cut
  138. ((memq lis1 lists) '()) ; Short cut
  139. (else (filter! (lambda (x)
  140. (every (lambda (lis) (not (s:member x lis =)))
  141. lists))
  142. lis1)))))
  143. (define (lset-xor = . lists)
  144. (check-arg procedure? = 'lset-xor)
  145. (reduce (lambda (b a) ; Compute A xor B:
  146. ;; Note that this code relies on the constant-time
  147. ;; short-cuts provided by LSET-DIFF+INTERSECTION,
  148. ;; LSET-DIFFERENCE & APPEND to provide constant-time short
  149. ;; cuts for the cases A = (), B = (), and A eq? B. It takes
  150. ;; a careful case analysis to see it, but it's carefully
  151. ;; built in.
  152. ;; Compute a-b and a^b, then compute b-(a^b) and
  153. ;; cons it onto the front of a-b.
  154. (let-values ([(a-b a-int-b) (lset-diff+intersection = a b)])
  155. (cond ((null? a-b) (lset-difference = b a))
  156. ((null? a-int-b) (append b a))
  157. (else (fold (lambda (xb ans)
  158. (if (s:member xb a-int-b =) ans (cons xb ans)))
  159. a-b
  160. b)))))
  161. '() lists))
  162. #; ; lists are immutable
  163. (define (lset-xor! = . lists)
  164. (check-arg procedure? = 'lset-xor!)
  165. (reduce (lambda (b a) ; Compute A xor B:
  166. ;; Note that this code relies on the constant-time
  167. ;; short-cuts provided by LSET-DIFF+INTERSECTION,
  168. ;; LSET-DIFFERENCE & APPEND to provide constant-time short
  169. ;; cuts for the cases A = (), B = (), and A eq? B. It takes
  170. ;; a careful case analysis to see it, but it's carefully
  171. ;; built in.
  172. ;; Compute a-b and a^b, then compute b-(a^b) and
  173. ;; cons it onto the front of a-b.
  174. (let-values ([(a-b a-int-b) (lset-diff+intersection! = a b)])
  175. (cond ((null? a-b) (lset-difference! = b a))
  176. ((null? a-int-b) (append! b a))
  177. (else (pair-fold
  178. (lambda (b-pair ans)
  179. (if (s:member (car b-pair) a-int-b =) ans
  180. (begin (set-cdr! b-pair ans) b-pair)))
  181. a-b
  182. b)))))
  183. '() lists))
  184. (define (lset-diff+intersection = lis1 . lists)
  185. (check-arg procedure? = 'lset-diff+intersection)
  186. (cond ((every null-list? lists) (values lis1 '())) ; Short cut
  187. ((memq lis1 lists) (values '() lis1)) ; Short cut
  188. (else (partition (lambda (elt)
  189. (not (any (lambda (lis) (s:member elt lis =))
  190. lists)))
  191. lis1))))
  192. #; ; lists are immutable
  193. (define (lset-diff+intersection! = lis1 . lists)
  194. (check-arg procedure? = 'lset-diff+intersection!)
  195. (cond ((every null-list? lists) (values lis1 '())) ; Short cut
  196. ((memq lis1 lists) (values '() lis1)) ; Short cut
  197. (else (partition! (lambda (elt)
  198. (not (any (lambda (lis) (s:member elt lis =))
  199. lists)))
  200. lis1))))
  201. ;;; lset.ss ends here