implementation-01.scm 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. ;;; This is a purely functional FIFO (first in first out) queue data structure
  2. ;;; in GNU Guile. This queue implementation makes use of a front stack and back
  3. ;;; stack, which are exchanged, when the front stack is empty and one tries to
  4. ;;; access the head of the queue.
  5. ;;; Adapted from https://programmingpraxis.com/2013/11/08/two-stacks-make-a-queue/
  6. ;;; Changes I made to adapt the code to GNU Guile:
  7. ;;; - translation to GNU Guile (Scheme)
  8. ;;; - binding renamings for better readability,
  9. ;;; - imports of required modules
  10. ;;; - bug fixes (endless loop in case of dequeue-ing from empty queue)
  11. ;;; - some error handling
  12. ;;; - comments (there were none)
  13. ;;; - docstrings (there were none)
  14. (use-modules
  15. (ice-9 match)
  16. ;; SRFI-8 for receive form
  17. (srfi srfi-8))
  18. ;; A stack is implemented using a list.
  19. (define empty-stack '())
  20. (define (push stack x)
  21. "Push an element onto the stack by cons-ing it to the list, which is used as
  22. stack."
  23. (cons x stack))
  24. (define (pop stack)
  25. "Pop the top element of the stack, returning both, the top element and the
  26. updated stack."
  27. (values (car stack) (cdr stack)))
  28. ;; Checking whether a stack is empty is simply checking whether a list is empty.
  29. (define empty-stack? null?)
  30. (define (transfer src dst)
  31. "Transfer all element of one stack to the other stack, reversing their order,
  32. as we can only pop elements, which are on top of a stack and only push elements
  33. onto elements of a stack."
  34. (cond [(empty-stack? src) dst]
  35. [else
  36. (receive (x xs) (pop src)
  37. ;; Transfer the rest of the elements. Recur.
  38. (transfer xs (push dst x)))]))
  39. ;; Creating an empty queue means creating an empty front stack and empty back
  40. ;; stack.
  41. (define empty-queue (cons empty-stack empty-stack))
  42. (define (enqueue queue elem)
  43. "Enqueue an element x into the given queue."
  44. ;; First, via match, separate the front stack and back stack of the queue, so
  45. ;; that we can push the new element onto one of the stacks.
  46. (match queue
  47. ;; A queue is the pair of back stack and front stack. Push the element onto
  48. ;; the back stack and make a new queue.
  49. [(back-stack . front-stack)
  50. (cons (push back-stack elem)
  51. front-stack)]
  52. [_
  53. (error "enqueue got something else than a queue:" queue)]))
  54. (define (dequeue queue)
  55. "Dequeue the head of the queue."
  56. ;; First, via match, separate the front stack and back stack of the queue.
  57. (cond
  58. [(queue-empty? queue)
  59. (error "cannot dequeue from empty queue")]
  60. [else
  61. (match queue
  62. [(back-stack . front-stack)
  63. (cond
  64. ;; If the front stack is empty, transfer all elements from the back stack to
  65. ;; the front stack and then dequeue.
  66. [(empty-stack? front-stack)
  67. (dequeue
  68. (cons empty-stack
  69. (transfer back-stack front-stack)))]
  70. ;; Otherwise pop an element from the front stack and return both, the element
  71. ;; and the updated queue.
  72. [else
  73. (receive (elem updated-front-stack) (pop front-stack)
  74. (values elem (cons back-stack updated-front-stack)))])]
  75. [_
  76. (error "dequeue got something else than a queue:" queue)])]))
  77. (define (queue-empty? queue)
  78. "Check whether a queue is empty, by checking whether both stacks are empty."
  79. (and (null? (car queue))
  80. (null? (cdr queue))))