From f956094178311abd1b3bc3801cfcabe3a23446e7 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Wed, 15 May 2019 19:57:35 -0300 Subject: [PATCH] Seventh update on iterators - QUEUE --- examples/deque/iterator.c | 2 +- examples/queue/README.md | 1 + examples/queue/ice_cream.c | 2 +- examples/queue/iterator.c | 195 +++++++++++++++++++++++++++++++++++++ src/deque.h | 2 +- src/queue.h | 157 +++++++++++++++++++++-------- 6 files changed, 318 insertions(+), 41 deletions(-) create mode 100644 examples/queue/iterator.c diff --git a/examples/deque/iterator.c b/examples/deque/iterator.c index 640281e..7350103 100644 --- a/examples/deque/iterator.c +++ b/examples/deque/iterator.c @@ -30,7 +30,7 @@ int main(int argc, char const *argv[]) d_push_back_if(my_deque, i, i % 2 == 0); } - // Iterate from the start (index 0) to the end (index count - 1) of the deque + // Iterate from the start (front index) to the end (rear index) of the deque for (deque_iter it = my_deque->it_start(my_deque); // Initialize iterator 'it' to the start of the deque !d_iter_end(&it); // Conditional stop (until it is not at the end) d_iter_next(&it)) // Iterator goes one position forward diff --git a/examples/queue/README.md b/examples/queue/README.md index 5cc036d..128078a 100644 --- a/examples/queue/README.md +++ b/examples/queue/README.md @@ -1,3 +1,4 @@ # Queue examples * ice_cream.c - Example of what happens when you get late to the queue and the ice creams are sold out! +* iterator.c - A complete example of the queue iterator diff --git a/examples/queue/ice_cream.c b/examples/queue/ice_cream.c index 9678d85..225a7c9 100644 --- a/examples/queue/ice_cream.c +++ b/examples/queue/ice_cream.c @@ -7,10 +7,10 @@ * Leonardo Vencovsky (https://github.com/LeoVen) * */ +#include "queue.h" #include #include #include -#include "queue.h" QUEUE_GENERATE(q, queue, /* static */, const char *) diff --git a/examples/queue/iterator.c b/examples/queue/iterator.c new file mode 100644 index 0000000..cebc00d --- /dev/null +++ b/examples/queue/iterator.c @@ -0,0 +1,195 @@ +/** + * iterator.c + * + * Creation Date: 15/05/2019 + * + * Authors: + * Leonardo Vencovsky (https://github.com/LeoVen) + * + * Iterator Example + */ +#include "queue.h" +#include +#include + +// Creates a queue of integers +QUEUE_GENERATE(q, queue, /* FMOD */, int) + +int main(int argc, char const *argv[]) +{ + // Create a new queue with an initial capacity of 100 + queue *my_queue = q_new(100); + + // Add elements to the queue + for (int i = 0; i < 20; i++) + { + q_enqueue(my_queue, i); + } + + // Iterate from the start (front) to the end (rear) of the queue + for (queue_iter it = my_queue->it_start(my_queue); // Initialize iterator 'it' to the start of the queue + !q_iter_end(&it); // Conditional stop (until it is not at the end) + q_iter_next(&it)) // Iterator goes one position forward + { + // Access the current element + int e = q_iter_value(&it); + + // Access the current index (index is relative to all the elements in the iteration) + size_t i = q_iter_index(&it); + + // Print queue + + // Index is useful in situations like this + // If it is the first element + if (i == 0) + printf("[ %d, ", e); + // If it is the last element + else if (i == q_count(my_queue) - 1) + printf("%d ]\n", e); + // For elements at the middle + else + printf("%d, ", e); + } + + // Index can also be used to iterate over a certain portion of a collection + size_t limit = q_count(my_queue) / 2; + for (queue_iter it = my_queue->it_start(my_queue); !q_iter_end(&it); q_iter_next(&it)) + { + // Access the current element + int e = q_iter_value(&it); + + // Access the current index (index is relative to all the elements in the iteration) + size_t i = q_iter_index(&it); + + // Print queue up until limit + + // If it is the first element + if (i == 0) + printf("[ %d, ", e); + // If it is the last element + else if (i == limit - 1) // -1 because index is 0 based and we want half of the queue + { + printf("%d ]\n", e); + break; + } + // For elements at the middle + else + printf("%d, ", e); + } + + // Going backwards + for (queue_iter it = my_queue->it_end(my_queue); // Initialize iterator 'it' to the end of the queue + !q_iter_start(&it); // Conditional stop (until it is not at the start) + q_iter_prev(&it)) // Iterator goes one position backwards + { + // Access the current element + int e = q_iter_value(&it); + + // Access the current index (index is relative to all the elements in the iteration) + size_t i = q_iter_index(&it); + + // Print queue + // Note how the conditions for the first and last elements are simply swapped + // Because iterating backwards make the first element be the index count - 1 + // and the last one be the index 0 + + // If it is the first element + if (i == q_count(my_queue) - 1) + printf("[ %d, ", e); + // If it is the last element + else if (i == 0) + printf("%d ]\n", e); + // For elements at the middle + else + printf("%d, ", e); + } + + // You can also use iterators in a while loop + // But to do so you must declare the iterator struct outside the loop first + queue_iter it; + + // You have three options: + // - Initialize it using the functions provided by the queue [ my_queue->it_start() and my_queue->it_end() ] + // - Initialize it using the init() function (when the iterator is already allocated) + // - Initialize it using the new() function (allocates on the heap, pointers only) + + it = my_queue->it_start(my_queue); + it = my_queue->it_end(my_queue); + + // This is equivalent to it_start() + q_iter_init(&it, my_queue); + + // For pointers + // Also equivalent to it_start() + queue_iter *iter = q_iter_new(my_queue); + + // The actual while loop + while (!q_iter_end(iter)) + { + // Accessing elements + int e = q_iter_value(iter); + size_t i = q_iter_index(iter); + + // Print index and element + printf("queue[%2" PRIuMAX "] = %2d\n", i, e); + + // Move iterator forwards + q_iter_next(iter); + } + + // Send the iterator to the start of the queue + q_iter_to_start(iter); + + // Send the iterator to the end of the queue + q_iter_to_end(iter); + + // Backwards loop + while (!q_iter_start(iter)) + { + // Accessing elements + int e = q_iter_value(iter); + size_t i = q_iter_index(iter); + + // Print index and element + printf("queue[%2" PRIuMAX "] = %2d\n", i, e); + + // Move iterator backwards + q_iter_prev(iter); + } + + // Certain iterators also allow you to modify the contents it is iterating over + // Exceptions are collections that depend of the current state of the element + // like tree-based collections and heap (collections that have some kind of ordering). + // You also can't change the keys of an associative collection + + // Change every element that is divisible by 3 to the number 7 + for (q_iter_to_start(&it); !q_iter_end(&it); q_iter_next(&it)) + { + // Get a reference to the current value + int *r = q_iter_rvalue(&it); + + if (r != NULL && *r % 3 == 0) + *r = 7; + } + + // Now print it to see the difference + for (queue_iter it = my_queue->it_start(my_queue); !q_iter_end(&it); q_iter_next(&it)) + { + int e = q_iter_value(&it); + size_t i = q_iter_index(&it); + + if (i == 0) + printf("[ %d, ", e); + else if (i == q_count(my_queue) - 1) + printf("%d ]\n", e); + else + printf("%d, ", e); + } + + // Dispose the iterator called by q_iter_new() + q_iter_free(iter); + // Dispose the queue created + q_free(my_queue); + + return 0; +} diff --git a/src/deque.h b/src/deque.h index deab64b..2e415f9 100644 --- a/src/deque.h +++ b/src/deque.h @@ -68,7 +68,7 @@ \ } SNAME, *SNAME##_ptr; \ \ - /* Deque Structure */ \ + /* Deque Iterator */ \ typedef struct SNAME##_iter_s \ { \ /* Target deque */ \ diff --git a/src/queue.h b/src/queue.h index ff9eb2b..407ed1d 100644 --- a/src/queue.h +++ b/src/queue.h @@ -37,60 +37,103 @@ #define QUEUE_GENERATE_SOURCE_PUBLIC(PFX, SNAME, FMOD, K, V) \ QUEUE_GENERATE_SOURCE(PFX, SNAME, FMOD, V) /* STRUCT ********************************************************************/ -#define QUEUE_GENERATE_STRUCT(PFX, SNAME, FMOD, V) \ - \ - struct SNAME##_s \ - { \ - V *buffer; \ - size_t capacity; \ - size_t count; \ - size_t front; \ - size_t rear; \ - }; \ - \ - struct SNAME##_iter_s \ - { \ - struct SNAME##_s *target; \ - size_t cursor; \ - size_t index; \ - bool start; \ - bool end; \ - }; \ - \ +#define QUEUE_GENERATE_STRUCT(PFX, SNAME, FMOD, V) + /* HEADER ********************************************************************/ #define QUEUE_GENERATE_HEADER(PFX, SNAME, FMOD, V) \ \ - typedef struct SNAME##_s SNAME; \ - typedef struct SNAME##_iter_s SNAME##_iter; \ + /* Queue Structure */ \ + typedef struct SNAME##_s \ + { \ + /* Dynamic circular array of elements */ \ + V *buffer; \ \ + /* Current circular array capacity */ \ + size_t capacity; \ + \ + /* Current amount of elements */ \ + size_t count; \ + \ + /* Index representing the front of the queue */ \ + size_t front; \ + \ + /* Index representing the back of the queue */ \ + size_t rear; \ + \ + /* Function that returns an iterator to the start of the queue */ \ + struct SNAME##_iter_s (*it_start)(struct SNAME##_s *); \ + \ + /* Function that returns an iterator to the end of the queue */ \ + struct SNAME##_iter_s (*it_end)(struct SNAME##_s *); \ + \ + } SNAME, *SNAME##_ptr; \ + \ + /* Queue Iterator */ \ + typedef struct SNAME##_iter_s \ + { \ + /* Target queue */ \ + struct SNAME##_s *target; \ + \ + /* Cursor's position (index) */ \ + size_t cursor; \ + \ + /* Keeps track of relative index to the iteration of elements */ \ + size_t index; \ + \ + /* If the iterator has reached the start of the iteration */ \ + bool start; \ + \ + /* If the iterator has reached the end of the iteration */ \ + bool end; \ + \ + } SNAME##_iter, *SNAME##_iter_ptr; \ + \ + /* Collection Functions */ \ + /* Collection Allocation and Deallocation */ \ FMOD SNAME *PFX##_new(size_t size); \ FMOD void PFX##_clear(SNAME *_queue_); \ FMOD void PFX##_free(SNAME *_queue_); \ + /* Collection Input and Output */ \ FMOD bool PFX##_enqueue(SNAME *_queue_, V element); \ FMOD bool PFX##_dequeue(SNAME *_queue_); \ + /* Conditional Input and Output */ \ FMOD bool PFX##_enqueue_if(SNAME *_queue_, V element, bool condition); \ FMOD bool PFX##_dequeue_if(SNAME *_queue_, bool condition); \ + /* Element Access */ \ FMOD V PFX##_peek(SNAME *_queue_); \ + /* Collection State */ \ FMOD bool PFX##_contains(SNAME *_queue_, V element, int (*comparator)(V, V)); \ FMOD bool PFX##_empty(SNAME *_queue_); \ FMOD bool PFX##_full(SNAME *_queue_); \ FMOD size_t PFX##_count(SNAME *_queue_); \ FMOD size_t PFX##_capacity(SNAME *_queue_); \ \ + /* Iterator Functions */ \ + /* Iterator Allocation and Deallocation */ \ FMOD SNAME##_iter *PFX##_iter_new(SNAME *target); \ FMOD void PFX##_iter_free(SNAME##_iter *iter); \ + /* Iterator Initialization */ \ FMOD void PFX##_iter_init(SNAME##_iter *iter, SNAME *target); \ + /* Iterator State */ \ FMOD bool PFX##_iter_start(SNAME##_iter *iter); \ FMOD bool PFX##_iter_end(SNAME##_iter *iter); \ - FMOD void PFX##_iter_tostart(SNAME##_iter *iter); \ - FMOD void PFX##_iter_toend(SNAME##_iter *iter); \ - FMOD bool PFX##_iter_next(SNAME##_iter *iter, V *result, size_t *index); \ - FMOD bool PFX##_iter_prev(SNAME##_iter *iter, V *result, size_t *index); \ + /* Iterator Movement */ \ + FMOD void PFX##_iter_to_start(SNAME##_iter *iter); \ + FMOD void PFX##_iter_to_end(SNAME##_iter *iter); \ + FMOD bool PFX##_iter_next(SNAME##_iter *iter); \ + FMOD bool PFX##_iter_prev(SNAME##_iter *iter); \ + /* Iterator Access */ \ + FMOD V PFX##_iter_value(SNAME##_iter *iter); \ + FMOD V *PFX##_iter_rvalue(SNAME##_iter *iter); \ + FMOD size_t PFX##_iter_index(SNAME##_iter *iter); \ \ /* SOURCE ********************************************************************/ #define QUEUE_GENERATE_SOURCE(PFX, SNAME, FMOD, V) \ \ + /* Implementation Detail Functions */ \ FMOD bool PFX##_grow(SNAME *_queue_); \ + SNAME##_iter PFX##_impl_it_start(SNAME *_list_); \ + SNAME##_iter PFX##_impl_it_end(SNAME *_list_); \ \ FMOD SNAME *PFX##_new(size_t size) \ { \ @@ -117,6 +160,9 @@ _queue_->front = 0; \ _queue_->rear = 0; \ \ + _queue_->it_start = PFX##_impl_it_start; \ + _queue_->it_end = PFX##_impl_it_end; \ + \ return _queue_; \ } \ \ @@ -255,7 +301,7 @@ return PFX##_empty(iter->target) || iter->end; \ } \ \ - FMOD void PFX##_iter_tostart(SNAME##_iter *iter) \ + FMOD void PFX##_iter_to_start(SNAME##_iter *iter) \ { \ iter->cursor = iter->target->front; \ iter->index = 0; \ @@ -263,7 +309,7 @@ iter->end = PFX##_empty(iter->target); \ } \ \ - FMOD void PFX##_iter_toend(SNAME##_iter *iter) \ + FMOD void PFX##_iter_to_end(SNAME##_iter *iter) \ { \ if (PFX##_empty(iter->target)) \ iter->cursor = 0; \ @@ -275,15 +321,12 @@ iter->end = true; \ } \ \ - FMOD bool PFX##_iter_next(SNAME##_iter *iter, V *result, size_t *index) \ + FMOD bool PFX##_iter_next(SNAME##_iter *iter) \ { \ if (iter->end) \ return false; \ \ - *index = iter->index; \ - *result = iter->target->buffer[iter->cursor]; \ - \ - iter->start = false; \ + iter->start = PFX##_empty(iter->target); \ \ if (iter->index == iter->target->count - 1) \ iter->end = true; \ @@ -296,15 +339,12 @@ return true; \ } \ \ - FMOD bool PFX##_iter_prev(SNAME##_iter *iter, V *result, size_t *index) \ + FMOD bool PFX##_iter_prev(SNAME##_iter *iter) \ { \ if (iter->start) \ return false; \ \ - *index = iter->index; \ - *result = iter->target->buffer[iter->cursor]; \ - \ - iter->end = false; \ + iter->end = PFX##_empty(iter->target); \ \ if (iter->index == 0) \ iter->start = true; \ @@ -317,6 +357,27 @@ return true; \ } \ \ + FMOD V PFX##_iter_value(SNAME##_iter *iter) \ + { \ + if (PFX##_empty(iter->target)) \ + return 0; \ + \ + return iter->target->buffer[iter->cursor]; \ + } \ + \ + FMOD V *PFX##_iter_rvalue(SNAME##_iter *iter) \ + { \ + if (PFX##_empty(iter->target)) \ + return NULL; \ + \ + return &(iter->target->buffer[iter->cursor]); \ + } \ + \ + FMOD size_t PFX##_iter_index(SNAME##_iter *iter) \ + { \ + return iter->index; \ + } \ + \ FMOD bool PFX##_grow(SNAME *_queue_) \ { \ \ @@ -340,6 +401,26 @@ _queue_->rear = _queue_->count; \ \ return true; \ + } \ + \ + SNAME##_iter PFX##_impl_it_start(SNAME *_list_) \ + { \ + SNAME##_iter iter; \ + \ + PFX##_iter_init(&iter, _list_); \ + PFX##_iter_to_start(&iter); \ + \ + return iter; \ + } \ + \ + SNAME##_iter PFX##_impl_it_end(SNAME *_list_) \ + { \ + SNAME##_iter iter; \ + \ + PFX##_iter_init(&iter, _list_); \ + PFX##_iter_to_end(&iter); \ + \ + return iter; \ } #endif /* CMC_QUEUE_H */