sets.scm 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2015 Ludovic Courtès <ludo@gnu.org>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (guix sets)
  19. #:use-module (srfi srfi-1)
  20. #:use-module (srfi srfi-9)
  21. #:use-module (srfi srfi-26)
  22. #:use-module (ice-9 vlist)
  23. #:use-module (ice-9 match)
  24. #:export (set
  25. setq
  26. set?
  27. set-insert
  28. set-union
  29. set-contains?
  30. set->list
  31. list->set
  32. list->setq))
  33. ;;; Commentary:
  34. ;;;
  35. ;;; A simple (simplistic?) implementation of unordered persistent sets based
  36. ;;; on vhashes that seems to be good enough so far.
  37. ;;;
  38. ;;; Another option would be to use "bounded balance trees" (Adams 1992) as
  39. ;;; implemented by Ian Price in 'pfds', which has faster union etc. but needs
  40. ;;; an order on the objects of the set.
  41. ;;;
  42. ;;; Code:
  43. (define-record-type <set>
  44. (%make-set vhash insert ref)
  45. set?
  46. (vhash set-vhash)
  47. (insert set-insert-proc)
  48. (ref set-ref))
  49. (define %insert
  50. (cut vhash-cons <> #t <>))
  51. (define %insertq
  52. (cut vhash-consq <> #t <>))
  53. (define (set . args)
  54. "Return a set containing the ARGS, compared as per 'equal?'."
  55. (list->set args))
  56. (define (setq . args)
  57. "Return a set containing the ARGS, compared as per 'eq?'."
  58. (list->setq args))
  59. (define (list->set lst)
  60. "Return a set with the elements taken from LST. Elements of the set will be
  61. compared with 'equal?'."
  62. (%make-set (fold %insert vlist-null lst)
  63. %insert
  64. vhash-assoc))
  65. (define (list->setq lst)
  66. "Return a set with the elements taken from LST. Elements of the set will be
  67. compared with 'eq?'."
  68. (%make-set (fold %insertq vlist-null lst)
  69. %insertq
  70. vhash-assq))
  71. (define-inlinable (set-contains? set value)
  72. "Return #t if VALUE is a member of SET."
  73. (->bool ((set-ref set) value (set-vhash set))))
  74. (define (set-insert value set)
  75. "Insert VALUE into SET."
  76. (if (set-contains? set value)
  77. set
  78. (let ((vhash ((set-insert-proc set) value (set-vhash set))))
  79. (%make-set vhash (set-insert-proc set) (set-ref set)))))
  80. (define-inlinable (set-size set)
  81. "Return the number of elements in SET."
  82. (vlist-length (set-vhash set)))
  83. (define (set-union set1 set2)
  84. "Return the union of SET1 and SET2. Warning: this is linear in the number
  85. of elements of the smallest."
  86. (unless (eq? (set-insert-proc set1) (set-insert-proc set2))
  87. (error "set-union: incompatible sets"))
  88. (let* ((small (if (> (set-size set1) (set-size set2))
  89. set2 set1))
  90. (large (if (eq? small set1) set2 set1)))
  91. (vlist-fold (match-lambda*
  92. (((item . _) result)
  93. (set-insert item result)))
  94. large
  95. (set-vhash small))))
  96. (define (set->list set)
  97. "Return the list of elements of SET."
  98. (map (match-lambda
  99. ((key . _) key))
  100. (vlist->list (set-vhash set))))
  101. ;;; sets.scm ends here