alist-helpers.scm 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. (library (alist-helpers)
  2. (export alist-refs
  3. alist-set
  4. alist-item-key
  5. alist-item-value
  6. alist-find-item-by-value
  7. alist-find-item-by-key)
  8. (import (rnrs base)
  9. (only (guile)
  10. lambda* λ
  11. ;; command line args
  12. simple-format
  13. current-output-port)
  14. ;; GNU Guile batteries
  15. (ice-9 exceptions)
  16. ;; SRFIs
  17. ;; SRFI 1 - list procs
  18. (srfi srfi-1)
  19. ;; SRFI 43 - vector procs
  20. (srfi srfi-43)
  21. ;; SRFI 69 - hash tables
  22. (srfi srfi-69))
  23. (define rest cdr)
  24. (define alist-item-key
  25. (λ (item)
  26. "Get the key of the given ITEM of an alist."
  27. (first item)))
  28. (define alist-item-value
  29. (λ (item)
  30. "Get the value of the given ITEM of an alist."
  31. (cdr item)))
  32. (define alist-find-item-by-value
  33. (lambda* (val alst #:key (equal-test equal?))
  34. "Find an item of the given alist ALST by given value VAL
  35. and return it."
  36. (find (λ (entry)
  37. (equal-test (alist-item-value entry) val))
  38. alst)))
  39. (define alist-find-item-by-key
  40. (lambda* (key alst #:key (equal-test equal?))
  41. "Find an item of the given alist ALST by given key KEY
  42. and return it."
  43. (find (λ (entry)
  44. (equal-test (first entry) key))
  45. alst)))
  46. (define alist-refs
  47. (lambda* (alist refs #:key (default-thunk #f) (equal-test equal?))
  48. ;; (simple-format (current-output-port) "remaining refs for alist: ~a\n" refs)
  49. (cond
  50. ;; If no more refs are given, we must have found what
  51. ;; we are looking for.
  52. [(null? refs) alist]
  53. [(pair? alist)
  54. (cond
  55. ;; If there are no more entries in the current alist,
  56. ;; then we could not find the searched key.
  57. [(null? alist)
  58. ;; If a default thunk was given, call it, otherwise
  59. ;; raise an exception.
  60. (if default-thunk
  61. (default-thunk)
  62. (raise-exception
  63. (make-exception (make-non-continuable-error)
  64. (make-exception-with-message "key not found")
  65. (make-exception-with-irritants (list refs alist))
  66. (make-exception-with-origin 'alist-refs))))]
  67. [else
  68. (let ([ref (first refs)]
  69. [item (first alist)])
  70. (cond
  71. [(equal-test (alist-item-key item) ref)
  72. (alist-refs (alist-item-value item)
  73. (drop refs 1)
  74. #:default-thunk default-thunk
  75. #:equal-test equal-test)]
  76. [else
  77. (alist-refs (drop alist 1)
  78. refs
  79. #:default-thunk default-thunk
  80. #:equal-test equal-test)]))])]
  81. [else
  82. ;; (simple-format (current-output-port) "not a pair: ~a\n" alist)
  83. (if default-thunk
  84. (default-thunk)
  85. (raise-exception
  86. (make-exception (make-non-continuable-error)
  87. (make-exception-with-message "key not found")
  88. (make-exception-with-irritants (list refs alist))
  89. (make-exception-with-origin 'alist-refs))))])))
  90. (define alist-set
  91. (lambda* (alst key val #:key (equal-test equal?))
  92. "Set a given value VAL for a given KEY in the given
  93. association list ALST."
  94. (cond
  95. [(null? alst) (cons (cons key val) '())]
  96. [else
  97. (let ([current-assoc (first alst)])
  98. (cond
  99. [(equal-test (alist-item-key current-assoc) key)
  100. (cons (cons key val)
  101. (drop alst 1))]
  102. [else
  103. (cons current-assoc
  104. (alist-set (drop alst 1)
  105. key
  106. val
  107. #:equal-test equal-test))]))])))
  108. (define alist?-shallow
  109. (λ (lst)
  110. "Check, whether LST is an association list, by only looking
  111. at the first item."
  112. (cond
  113. [(null? lst) #t]
  114. [(pair? lst)
  115. (pair? (first lst))]
  116. [else #f])))
  117. (define alist-any-key
  118. (lambda* (alst pred)
  119. "Check, whether any key in the alist ALST satisfies the
  120. given predicate PRED."
  121. (cond
  122. [(null? alst) #f]
  123. [else
  124. (let ([first-key (alist-item-key (first alst))]
  125. [first-val (alist-item-value (first alst))])
  126. (cond
  127. ;; Check the predicate for the first key.
  128. [(pred first-key) #t]
  129. ;; If the first value seems to be an association
  130. ;; list itself, then check it and the rest of the
  131. ;; alist keys at the current level.
  132. [(alist?-shallow first-val)
  133. (or (alist-any-key first-val pred)
  134. (alist-any-key (drop alst 1) pred))]
  135. [else
  136. ;; Check the rest of the keys of the association
  137. ;; list.
  138. (alist-any-key (drop alst 1) pred)]))])))
  139. (define alist-any-value
  140. (λ (alst pred)
  141. "Check, whether any value in the alist ALST satisfies the
  142. given predicate PRED."
  143. (cond
  144. [(null? alst) #f]
  145. [else
  146. (let ([first-val (alist-item-value (first alst))])
  147. (cond
  148. [(pred first-val) #t]
  149. [(alist?-shallow first-val)
  150. (or (alist-any-value first-val pred)
  151. (alist-any-value (drop alst 1) pred))]
  152. [else
  153. (alist-any-value (drop alst 1) pred)]))])))
  154. (define alist-set*
  155. (lambda* (alst keys val #:key (equal-test equal?))
  156. "Set value VAL inside the alist ALST navigating through its
  157. keys using KEYS to get to the place where VAL shall be the
  158. new value."
  159. (define traverse
  160. (λ (alst keys)
  161. (cond
  162. [(null? keys) val]
  163. [(not (alist?-shallow alst))
  164. (raise-exception
  165. (make-exception (make-non-continuable-error)
  166. (make-exception-with-message "key not found")
  167. (make-exception-with-irritants keys)
  168. (make-exception-with-origin 'alist-set*)))]
  169. [(null? alst) (cons (cons (first keys)
  170. val)
  171. '())]
  172. [else
  173. (let ([current-assoc (first alst)]
  174. [item-key (alist-item-key (first alst))])
  175. (cond
  176. [(equal-test item-key (first keys))
  177. ;; Change the value and cons the rest of the list.
  178. (cons (cons item-key
  179. (traverse (alist-item-value current-assoc)
  180. (drop keys 1)))
  181. (drop alst 1))]
  182. [else
  183. (cons current-assoc
  184. (traverse (drop alst 1) keys))]))])))
  185. (traverse alst keys))))