vlist.scm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. ;;; -*- mode: scheme; coding: utf-8; -*-
  2. ;;;
  3. ;;; Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
  4. ;;;
  5. ;;; This library is free software; you can redistribute it and/or
  6. ;;; modify it under the terms of the GNU Lesser General Public
  7. ;;; License as published by the Free Software Foundation; either
  8. ;;; version 3 of the License, or (at your option) any later version.
  9. ;;;
  10. ;;; This library is distributed in the hope that it will be useful,
  11. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. ;;; Lesser General Public License for more details.
  14. ;;;
  15. ;;; You should have received a copy of the GNU Lesser General Public
  16. ;;; License along with this library; if not, write to the Free Software
  17. ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. (define-module (ice-9 vlist)
  19. #:use-module (srfi srfi-1)
  20. #:use-module (srfi srfi-9)
  21. #:use-module (srfi srfi-9 gnu)
  22. #:use-module (srfi srfi-26)
  23. #:use-module (ice-9 format)
  24. #:export (vlist? vlist-cons vlist-head vlist-tail vlist-null?
  25. vlist-null list->vlist vlist-ref vlist-drop vlist-take
  26. vlist-length vlist-fold vlist-fold-right vlist-map
  27. vlist-unfold vlist-unfold-right vlist-append
  28. vlist-reverse vlist-filter vlist-delete vlist->list
  29. vlist-for-each
  30. block-growth-factor
  31. vhash? vhash-cons vhash-consq vhash-consv
  32. vhash-assoc vhash-assq vhash-assv
  33. vhash-delete vhash-delq vhash-delv
  34. vhash-fold vhash-fold-right
  35. vhash-fold* vhash-foldq* vhash-foldv*
  36. alist->vhash))
  37. ;;; Author: Ludovic Courtès <ludo@gnu.org>
  38. ;;;
  39. ;;; Commentary:
  40. ;;;
  41. ;;; This module provides an implementations of vlists, a functional list-like
  42. ;;; data structure described by Phil Bagwell in "Fast Functional Lists,
  43. ;;; Hash-Lists, Dequeues and Variable-Length Arrays", EPFL Technical Report,
  44. ;;; 2002.
  45. ;;;
  46. ;;; The idea is to store vlist elements in increasingly large contiguous blocks
  47. ;;; (implemented as vectors here). These blocks are linked to one another using
  48. ;;; a pointer to the next block (called `block-base' here) and an offset within
  49. ;;; that block (`block-offset' here). The size of these blocks form a geometric
  50. ;;; series with ratio `block-growth-factor'.
  51. ;;;
  52. ;;; In the best case (e.g., using a vlist returned by `list->vlist'),
  53. ;;; elements from the first half of an N-element vlist are accessed in O(1)
  54. ;;; (assuming `block-growth-factor' is 2), and `vlist-length' takes only
  55. ;;; O(ln(N)). Furthermore, the data structure improves data locality since
  56. ;;; vlist elements are adjacent, which plays well with caches.
  57. ;;;
  58. ;;; Code:
  59. ;;;
  60. ;;; VList Blocks and Block Descriptors.
  61. ;;;
  62. (define block-growth-factor
  63. (make-fluid 2))
  64. (define-inlinable (make-block base offset size hash-tab?)
  65. ;; Return a block (and block descriptor) of SIZE elements pointing to
  66. ;; BASE at OFFSET. If HASH-TAB? is true, we also reserve space for a
  67. ;; "hash table". Note: We use `next-free' instead of `last-used' as
  68. ;; suggested by Bagwell.
  69. (if hash-tab?
  70. (vector (make-vector (* size 3) #f)
  71. base offset size 0)
  72. (vector (make-vector size)
  73. base offset size 0)))
  74. (define-syntax-rule (define-block-accessor name index)
  75. (define-inlinable (name block)
  76. (vector-ref block index)))
  77. (define-block-accessor block-content 0)
  78. (define-block-accessor block-base 1)
  79. (define-block-accessor block-offset 2)
  80. (define-block-accessor block-size 3)
  81. (define-block-accessor block-next-free 4)
  82. (define-inlinable (block-hash-table? block)
  83. (< (block-size block) (vector-length (block-content block))))
  84. (define-inlinable (set-block-next-free! block next-free)
  85. (vector-set! block 4 next-free))
  86. (define-inlinable (block-append! block value offset)
  87. ;; This is not thread-safe. To fix it, see Section 2.8 of the paper.
  88. (and (< offset (block-size block))
  89. (= offset (block-next-free block))
  90. (begin
  91. (set-block-next-free! block (1+ offset))
  92. (vector-set! (block-content block) offset value)
  93. #t)))
  94. ;; Return the item at slot OFFSET.
  95. (define-inlinable (block-ref content offset)
  96. (vector-ref content offset))
  97. ;; Return the offset of the next item in the hash bucket, after the one
  98. ;; at OFFSET.
  99. (define-inlinable (block-hash-table-next-offset content size offset)
  100. (vector-ref content (+ size size offset)))
  101. ;; Save the offset of the next item in the hash bucket, after the one
  102. ;; at OFFSET.
  103. (define-inlinable (block-hash-table-set-next-offset! content size offset
  104. next-offset)
  105. (vector-set! content (+ size size offset) next-offset))
  106. ;; Returns the index of the last entry stored in CONTENT with
  107. ;; SIZE-modulo hash value KHASH.
  108. (define-inlinable (block-hash-table-ref content size khash)
  109. (vector-ref content (+ size khash)))
  110. (define-inlinable (block-hash-table-set! content size khash offset)
  111. (vector-set! content (+ size khash) offset))
  112. ;; Add hash table information for the item recently added at OFFSET,
  113. ;; with SIZE-modulo hash KHASH.
  114. (define-inlinable (block-hash-table-add! content size khash offset)
  115. (block-hash-table-set-next-offset! content size offset
  116. (block-hash-table-ref content size khash))
  117. (block-hash-table-set! content size khash offset))
  118. (define block-null
  119. ;; The null block.
  120. (make-block #f 0 0 #f))
  121. ;;;
  122. ;;; VLists.
  123. ;;;
  124. (define-record-type <vlist>
  125. ;; A vlist is just a base+offset pair pointing to a block.
  126. ;; XXX: Allocating a <vlist> record in addition to the block at each
  127. ;; `vlist-cons' call is inefficient. However, Bagwell's hack to avoid it
  128. ;; (Section 2.2) would require GC_ALL_INTERIOR_POINTERS, which would be a
  129. ;; performance hit for everyone.
  130. (make-vlist base offset)
  131. vlist?
  132. (base vlist-base)
  133. (offset vlist-offset))
  134. (set-record-type-printer! <vlist>
  135. (lambda (vl port)
  136. (cond ((vlist-null? vl)
  137. (format port "#<vlist ()>"))
  138. ((vhash? vl)
  139. (format port "#<vhash ~x ~a pairs>"
  140. (object-address vl)
  141. (vlist-length vl)))
  142. (else
  143. (format port "#<vlist ~a>"
  144. (vlist->list vl))))))
  145. (define vlist-null
  146. ;; The empty vlist.
  147. (make-vlist block-null 0))
  148. ;; Asserting that something is a vlist is actually a win if your next
  149. ;; step is to call record accessors, because that causes CSE to
  150. ;; eliminate the type checks in those accessors.
  151. ;;
  152. (define-inlinable (assert-vlist val)
  153. (unless (vlist? val)
  154. (throw 'wrong-type-arg
  155. #f
  156. "Not a vlist: ~S"
  157. (list val)
  158. (list val))))
  159. (define-inlinable (block-cons item vlist hash-tab?)
  160. (let ((base (vlist-base vlist))
  161. (offset (1+ (vlist-offset vlist))))
  162. (cond
  163. ((block-append! base item offset)
  164. ;; Fast path: We added the item directly to the block.
  165. (make-vlist base offset))
  166. (else
  167. ;; Slow path: Allocate a new block.
  168. (let* ((size (block-size base))
  169. (base (make-block
  170. base
  171. (1- offset)
  172. (cond
  173. ((zero? size) 1)
  174. ((< offset size) 1) ;; new vlist head
  175. (else (* (fluid-ref block-growth-factor) size)))
  176. hash-tab?)))
  177. (set-block-next-free! base 1)
  178. (vector-set! (block-content base) 0 item)
  179. (make-vlist base 0))))))
  180. (define (vlist-cons item vlist)
  181. "Return a new vlist with ITEM as its head and VLIST as its
  182. tail."
  183. ;; Note: Although the result of `vlist-cons' on a vhash is a valid
  184. ;; vlist, it is not a valid vhash. The new item does not get a hash
  185. ;; table entry. If we allocate a new block, the new block will not
  186. ;; have a hash table. Perhaps we can do something more sensible here,
  187. ;; but this is a hot function, so there are performance impacts.
  188. (assert-vlist vlist)
  189. (block-cons item vlist #f))
  190. (define (vlist-head vlist)
  191. "Return the head of VLIST."
  192. (assert-vlist vlist)
  193. (let ((base (vlist-base vlist))
  194. (offset (vlist-offset vlist)))
  195. (block-ref (block-content base) offset)))
  196. (define (vlist-tail vlist)
  197. "Return the tail of VLIST."
  198. (assert-vlist vlist)
  199. (let ((base (vlist-base vlist))
  200. (offset (vlist-offset vlist)))
  201. (if (> offset 0)
  202. (make-vlist base (- offset 1))
  203. (make-vlist (block-base base)
  204. (block-offset base)))))
  205. (define (vlist-null? vlist)
  206. "Return true if VLIST is empty."
  207. (assert-vlist vlist)
  208. (let ((base (vlist-base vlist)))
  209. (and (not (block-base base))
  210. (= 0 (block-size base)))))
  211. ;;;
  212. ;;; VList Utilities.
  213. ;;;
  214. (define (list->vlist lst)
  215. "Return a new vlist whose contents correspond to LST."
  216. (vlist-reverse (fold vlist-cons vlist-null lst)))
  217. (define (vlist-fold proc init vlist)
  218. "Fold over VLIST, calling PROC for each element."
  219. ;; FIXME: Handle multiple lists.
  220. (assert-vlist vlist)
  221. (let loop ((base (vlist-base vlist))
  222. (offset (vlist-offset vlist))
  223. (result init))
  224. (if (eq? base block-null)
  225. result
  226. (let* ((next (- offset 1))
  227. (done? (< next 0)))
  228. (loop (if done? (block-base base) base)
  229. (if done? (block-offset base) next)
  230. (proc (block-ref (block-content base) offset) result))))))
  231. (define (vlist-fold-right proc init vlist)
  232. "Fold over VLIST, calling PROC for each element, starting from
  233. the last element."
  234. (assert-vlist vlist)
  235. (let loop ((index (1- (vlist-length vlist)))
  236. (result init))
  237. (if (< index 0)
  238. result
  239. (loop (1- index)
  240. (proc (vlist-ref vlist index) result)))))
  241. (define (vlist-reverse vlist)
  242. "Return a new VLIST whose content are those of VLIST in reverse
  243. order."
  244. (vlist-fold vlist-cons vlist-null vlist))
  245. (define (vlist-map proc vlist)
  246. "Map PROC over the elements of VLIST and return a new vlist."
  247. (vlist-fold (lambda (item result)
  248. (vlist-cons (proc item) result))
  249. vlist-null
  250. (vlist-reverse vlist)))
  251. (define (vlist->list vlist)
  252. "Return a new list whose contents match those of VLIST."
  253. (vlist-fold-right cons '() vlist))
  254. (define (vlist-ref vlist index)
  255. "Return the element at index INDEX in VLIST."
  256. (assert-vlist vlist)
  257. (let loop ((index index)
  258. (base (vlist-base vlist))
  259. (offset (vlist-offset vlist)))
  260. (if (<= index offset)
  261. (block-ref (block-content base) (- offset index))
  262. (loop (- index offset 1)
  263. (block-base base)
  264. (block-offset base)))))
  265. (define (vlist-drop vlist count)
  266. "Return a new vlist that does not contain the COUNT first elements of
  267. VLIST."
  268. (assert-vlist vlist)
  269. (let loop ((count count)
  270. (base (vlist-base vlist))
  271. (offset (vlist-offset vlist)))
  272. (if (<= count offset)
  273. (make-vlist base (- offset count))
  274. (loop (- count offset 1)
  275. (block-base base)
  276. (block-offset base)))))
  277. (define (vlist-take vlist count)
  278. "Return a new vlist that contains only the COUNT first elements of
  279. VLIST."
  280. (let loop ((count count)
  281. (vlist vlist)
  282. (result vlist-null))
  283. (if (= 0 count)
  284. (vlist-reverse result)
  285. (loop (- count 1)
  286. (vlist-tail vlist)
  287. (vlist-cons (vlist-head vlist) result)))))
  288. (define (vlist-filter pred vlist)
  289. "Return a new vlist containing all the elements from VLIST that
  290. satisfy PRED."
  291. (vlist-fold-right (lambda (e v)
  292. (if (pred e)
  293. (vlist-cons e v)
  294. v))
  295. vlist-null
  296. vlist))
  297. (define* (vlist-delete x vlist #:optional (equal? equal?))
  298. "Return a new vlist corresponding to VLIST without the elements
  299. EQUAL? to X."
  300. (vlist-filter (lambda (e)
  301. (not (equal? e x)))
  302. vlist))
  303. (define (vlist-length vlist)
  304. "Return the length of VLIST."
  305. (assert-vlist vlist)
  306. (let loop ((base (vlist-base vlist))
  307. (len (vlist-offset vlist)))
  308. (if (eq? base block-null)
  309. len
  310. (loop (block-base base)
  311. (+ len 1 (block-offset base))))))
  312. (define* (vlist-unfold p f g seed
  313. #:optional (tail-gen (lambda (x) vlist-null)))
  314. "Return a new vlist. See the description of SRFI-1 `unfold' for details."
  315. (let uf ((seed seed))
  316. (if (p seed)
  317. (tail-gen seed)
  318. (vlist-cons (f seed)
  319. (uf (g seed))))))
  320. (define* (vlist-unfold-right p f g seed #:optional (tail vlist-null))
  321. "Return a new vlist. See the description of SRFI-1 `unfold-right' for
  322. details."
  323. (let uf ((seed seed) (lis tail))
  324. (if (p seed)
  325. lis
  326. (uf (g seed) (vlist-cons (f seed) lis)))))
  327. (define (vlist-append . vlists)
  328. "Append the given lists."
  329. (if (null? vlists)
  330. vlist-null
  331. (fold-right (lambda (vlist result)
  332. (vlist-fold-right (lambda (e v)
  333. (vlist-cons e v))
  334. result
  335. vlist))
  336. vlist-null
  337. vlists)))
  338. (define (vlist-for-each proc vlist)
  339. "Call PROC on each element of VLIST. The result is unspecified."
  340. (vlist-fold (lambda (item x)
  341. (proc item))
  342. (if #f #f)
  343. vlist))
  344. ;;;
  345. ;;; Hash Lists, aka. `VHash'.
  346. ;;;
  347. ;; Assume keys K1 and K2, H = hash(K1) = hash(K2), and two values V1 and V2
  348. ;; associated with K1 and K2, respectively. The resulting layout is a
  349. ;; follows:
  350. ;;
  351. ;; ,--------------------.
  352. ;; 0| ,-> (K1 . V1) | Vlist array
  353. ;; 1| | |
  354. ;; 2| | (K2 . V2) |
  355. ;; 3| | |
  356. ;; size +-|------------------+
  357. ;; 0| | | Hash table
  358. ;; 1| | |
  359. ;; 2| +-- O <------------- H
  360. ;; 3| | |
  361. ;; size * 2 +-|------------------+
  362. ;; 0| `-> 2 | Chain links
  363. ;; 1| |
  364. ;; 2| #f |
  365. ;; 3| |
  366. ;; size * 3 `--------------------'
  367. ;;
  368. ;; The backing store for the vhash is partitioned into three areas: the
  369. ;; vlist part, the hash table part, and the chain links part. In this
  370. ;; example we have a hash H which, when indexed into the hash table
  371. ;; part, indicates that a value with this hash can be found at offset 0
  372. ;; in the vlist part. The corresponding index (in this case, 0) of the
  373. ;; chain links array holds the index of the next element in this block
  374. ;; with this hash value, or #f if we reached the end of the chain.
  375. ;;
  376. ;; This API potentially requires users to repeat which hash function and
  377. ;; which equality predicate to use. This can lead to unpredictable
  378. ;; results if they are used in consistenly, e.g., between `vhash-cons'
  379. ;; and `vhash-assoc', which is undesirable, as argued in
  380. ;; http://savannah.gnu.org/bugs/?22159 . OTOH, two arguments can be
  381. ;; made in favor of this API:
  382. ;;
  383. ;; - It's consistent with how alists are handled in SRFI-1.
  384. ;;
  385. ;; - In practice, users will probably consistenly use either the `q',
  386. ;; the `v', or the plain variant (`vlist-cons' and `vlist-assoc'
  387. ;; without any optional argument), i.e., they will rarely explicitly
  388. ;; pass a hash function or equality predicate.
  389. (define (vhash? obj)
  390. "Return true if OBJ is a hash list."
  391. (and (vlist? obj)
  392. (block-hash-table? (vlist-base obj))))
  393. (define* (vhash-cons key value vhash #:optional (hash hash))
  394. "Return a new hash list based on VHASH where KEY is associated
  395. with VALUE. Use HASH to compute KEY's hash."
  396. (assert-vlist vhash)
  397. ;; We should also assert that it is a hash table. Need to check the
  398. ;; performance impacts of that. Also, vlist-null is a valid hash
  399. ;; table, which does not pass vhash?. A bug, perhaps.
  400. (let* ((vhash (block-cons (cons key value) vhash #t))
  401. (base (vlist-base vhash))
  402. (offset (vlist-offset vhash))
  403. (size (block-size base))
  404. (khash (hash key size))
  405. (content (block-content base)))
  406. (block-hash-table-add! content size khash offset)
  407. vhash))
  408. (define vhash-consq (cut vhash-cons <> <> <> hashq))
  409. (define vhash-consv (cut vhash-cons <> <> <> hashv))
  410. (define-inlinable (%vhash-fold* proc init key vhash equal? hash)
  411. ;; Fold over all the values associated with KEY in VHASH.
  412. (define (visit-block base max-offset result)
  413. (let* ((size (block-size base))
  414. (content (block-content base))
  415. (khash (hash key size)))
  416. (let loop ((offset (block-hash-table-ref content size khash))
  417. (result result))
  418. (if offset
  419. (loop (block-hash-table-next-offset content size offset)
  420. (if (and (<= offset max-offset)
  421. (equal? key (car (block-ref content offset))))
  422. (proc (cdr (block-ref content offset)) result)
  423. result))
  424. (let ((next-block (block-base base)))
  425. (if (> (block-size next-block) 0)
  426. (visit-block next-block (block-offset base) result)
  427. result))))))
  428. (assert-vlist vhash)
  429. (if (> (block-size (vlist-base vhash)) 0)
  430. (visit-block (vlist-base vhash)
  431. (vlist-offset vhash)
  432. init)
  433. init))
  434. (define* (vhash-fold* proc init key vhash
  435. #:optional (equal? equal?) (hash hash))
  436. "Fold over all the values associated with KEY in VHASH, with each
  437. call to PROC having the form ‘(proc value result)’, where
  438. RESULT is the result of the previous call to PROC and INIT the
  439. value of RESULT for the first call to PROC."
  440. (%vhash-fold* proc init key vhash equal? hash))
  441. (define (vhash-foldq* proc init key vhash)
  442. "Same as ‘vhash-fold*’, but using ‘hashq’ and ‘eq?’."
  443. (%vhash-fold* proc init key vhash eq? hashq))
  444. (define (vhash-foldv* proc init key vhash)
  445. "Same as ‘vhash-fold*’, but using ‘hashv’ and ‘eqv?’."
  446. (%vhash-fold* proc init key vhash eqv? hashv))
  447. (define-inlinable (%vhash-assoc key vhash equal? hash)
  448. ;; A specialization of `vhash-fold*' that stops when the first value
  449. ;; associated with KEY is found or when the end-of-list is reached. Inline to
  450. ;; make sure `vhash-assq' gets to use the `eq?' instruction instead of calling
  451. ;; the `eq?' subr.
  452. (define (visit-block base max-offset)
  453. (let* ((size (block-size base))
  454. (content (block-content base))
  455. (khash (hash key size)))
  456. (let loop ((offset (block-hash-table-ref content size khash)))
  457. (if offset
  458. (if (and (<= offset max-offset)
  459. (equal? key (car (block-ref content offset))))
  460. (block-ref content offset)
  461. (loop (block-hash-table-next-offset content size offset)))
  462. (let ((next-block (block-base base)))
  463. (and (> (block-size next-block) 0)
  464. (visit-block next-block (block-offset base))))))))
  465. (assert-vlist vhash)
  466. (and (> (block-size (vlist-base vhash)) 0)
  467. (visit-block (vlist-base vhash)
  468. (vlist-offset vhash))))
  469. (define* (vhash-assoc key vhash #:optional (equal? equal?) (hash hash))
  470. "Return the first key/value pair from VHASH whose key is equal to
  471. KEY according to the EQUAL? equality predicate."
  472. (%vhash-assoc key vhash equal? hash))
  473. (define (vhash-assq key vhash)
  474. "Return the first key/value pair from VHASH whose key is ‘eq?’ to
  475. KEY."
  476. (%vhash-assoc key vhash eq? hashq))
  477. (define (vhash-assv key vhash)
  478. "Return the first key/value pair from VHASH whose key is ‘eqv?’ to
  479. KEY."
  480. (%vhash-assoc key vhash eqv? hashv))
  481. (define* (vhash-delete key vhash #:optional (equal? equal?) (hash hash))
  482. "Remove all associations from VHASH with KEY, comparing keys
  483. with EQUAL?."
  484. (if (vhash-assoc key vhash equal? hash)
  485. (vlist-fold (lambda (k+v result)
  486. (let ((k (car k+v))
  487. (v (cdr k+v)))
  488. (if (equal? k key)
  489. result
  490. (vhash-cons k v result hash))))
  491. vlist-null
  492. vhash)
  493. vhash))
  494. (define vhash-delq (cut vhash-delete <> <> eq? hashq))
  495. (define vhash-delv (cut vhash-delete <> <> eqv? hashv))
  496. (define (vhash-fold proc init vhash)
  497. "Fold over the key/pair elements of VHASH from left to right, with
  498. each call to PROC having the form ‘(PROC key value result)’,
  499. where RESULT is the result of the previous call to PROC and
  500. INIT the value of RESULT for the first call to PROC."
  501. (vlist-fold (lambda (key+value result)
  502. (proc (car key+value) (cdr key+value)
  503. result))
  504. init
  505. vhash))
  506. (define (vhash-fold-right proc init vhash)
  507. "Fold over the key/pair elements of VHASH from right to left, with
  508. each call to PROC having the form ‘(PROC key value result)’,
  509. where RESULT is the result of the previous call to PROC and
  510. INIT the value of RESULT for the first call to PROC."
  511. (vlist-fold-right (lambda (key+value result)
  512. (proc (car key+value) (cdr key+value)
  513. result))
  514. init
  515. vhash))
  516. (define* (alist->vhash alist #:optional (hash hash))
  517. "Return the vhash corresponding to ALIST, an association list."
  518. (fold-right (lambda (pair result)
  519. (vhash-cons (car pair) (cdr pair) result hash))
  520. vlist-null
  521. alist))
  522. ;;; vlist.scm ends here