From 444f597a9857215fee8a04a6fda089d1b1c6287c Mon Sep 17 00:00:00 2001 From: Leonardo Date: Sat, 11 May 2019 00:24:48 -0300 Subject: [PATCH] First update on iterators - LIST --- examples/iterators/list.c | 24 +++-- examples/list/README.md | 1 + examples/list/iterator.c | 195 ++++++++++++++++++++++++++++++++++++++ examples/list/list.c | 11 ++- examples/list/string.c | 38 ++++---- src/list.h | 168 +++++++++++++++++++++++--------- 6 files changed, 361 insertions(+), 76 deletions(-) create mode 100644 examples/list/iterator.c diff --git a/examples/iterators/list.c b/examples/iterators/list.c index 07023dd..5527b71 100644 --- a/examples/iterators/list.c +++ b/examples/iterators/list.c @@ -30,26 +30,30 @@ int main(int argc, char const *argv[]) // Make the iterator go back and forward for (int j = 0; j < 4; j++) { - while (l_iter_next(&iter, &r, &i)) - printf("C[%2d] = %2d\n", i, r); + while (!l_iter_end(&iter)) + { + printf("C[%2d] = %2d\n", l_iter_index(&iter), l_iter_value(&iter)); + l_iter_next(&iter); + } printf("\n"); - while (l_iter_prev(&iter, &r, &i)) - printf("C[%2d] = %2d\n", i, r); + while (!l_iter_start(&iter)) + { + printf("C[%2d] = %2d\n", l_iter_index(&iter), l_iter_value(&iter)); + l_iter_prev(&iter); + } printf("\n\n"); } - for (l_iter_tostart(&iter); !l_iter_end(&iter); /**/) + for (l_iter_to_start(&iter); !l_iter_end(&iter); l_iter_next(&iter)) { - l_iter_next(&iter, &r, &i); - printf("L[%2d] = %2d\n", i, r); + printf("L[%2d] = %2d\n", l_iter_index(&iter), l_iter_value(&iter)); } printf("\n"); - for (l_iter_toend(&iter); !l_iter_start(&iter); /**/) + for (l_iter_to_end(&iter); !l_iter_start(&iter); l_iter_prev(&iter)) { - l_iter_prev(&iter, &r, &i); - printf("L[%2d] = %2d\n", i, r); + printf("L[%2d] = %2d\n", l_iter_index(&iter), l_iter_value(&iter)); } l_free(l); diff --git a/examples/list/README.md b/examples/list/README.md index 75ea3b9..58049cf 100644 --- a/examples/list/README.md +++ b/examples/list/README.md @@ -1,4 +1,5 @@ # List examples +* iterator.c - A complete example of the list iterator * list.c - Example of using a list and its iterator. * string.c - How to treat a list as a mutable string. diff --git a/examples/list/iterator.c b/examples/list/iterator.c new file mode 100644 index 0000000..db1c23d --- /dev/null +++ b/examples/list/iterator.c @@ -0,0 +1,195 @@ +/** + * iterator.c + * + * Creation Date: 10/05/2019 + * + * Authors: + * Leonardo Vencovsky (https://github.com/LeoVen) + * + * Iterator Example + */ +#include "list.h" +#include +#include + +// Creates a list of integers +LIST_GENERATE(l, list, /* FMOD */, int) + +int main(int argc, char const *argv[]) +{ + // Create a new list with an initial capacity of 100 + list *my_list = l_new(100); + + // Add elements to the list + for (int i = 0; i < 20; i++) + { + l_push_back(my_list, i); + } + + // Iterate from the start (index 0) to the end (index count - 1) of the list + for (list_iter it = my_list->it_start(my_list); // Initialize iterator 'it' to the start of the list + !l_iter_end(&it); // Conditional stop (until it is not at the end) + l_iter_next(&it)) // Iterator goes one position forward + { + // Access the current element + int e = l_iter_value(&it); + + // Access the current index (index is relative to all the elements in the iteration) + size_t i = l_iter_index(&it); + + // Print list + + // 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 == l_count(my_list) - 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 = l_count(my_list) / 2; + for (list_iter it = my_list->it_start(my_list); !l_iter_end(&it); l_iter_next(&it)) + { + // Access the current element + int e = l_iter_value(&it); + + // Access the current index (index is relative to all the elements in the iteration) + size_t i = l_iter_index(&it); + + // Print list 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) + { + printf("%d ]\n", e); + break; + } + // For elements at the middle + else + printf("%d, ", e); + } + + // Going backwards + for (list_iter it = my_list->it_end(my_list); // Initialize iterator 'it' to the end of the list + !l_iter_start(&it); // Conditional stop (until it is not at the start) + l_iter_prev(&it)) // Iterator goes one position backwards + { + // Access the current element + int e = l_iter_value(&it); + + // Access the current index (index is relative to all the elements in the iteration) + size_t i = l_iter_index(&it); + + // Print list + // 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 == l_count(my_list) - 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 + list_iter it; + + // You have three options: + // - Initialize it using the functions provided by the list [ my_list->it_start() and my_list->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_list->it_start(my_list); + it = my_list->it_end(my_list); + + // This is equivalent to it_start() + l_iter_init(&it, my_list); + + // For pointers + // Also equivalent to it_start() + list_iter *iter = l_iter_new(my_list); + + // The actual while loop + while (!l_iter_end(iter)) + { + // Accessing elements + int e = l_iter_value(iter); + size_t i = l_iter_index(iter); + + // Print index and element + printf("list[%2" PRIuMAX "] = %2d\n", i, e); + + // Move iterator forwards + l_iter_next(iter); + } + + // Send the iterator to the start of the list + l_iter_to_start(iter); + + // Send the iterator to the end of the list + l_iter_to_end(iter); + + // Backwards loop + while (!l_iter_start(iter)) + { + // Accessing elements + int e = l_iter_value(iter); + size_t i = l_iter_index(iter); + + // Print index and element + printf("list[%2" PRIuMAX "] = %2d\n", i, e); + + // Move iterator backwards + l_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 2 to the number 7 + for (l_iter_to_start(&it); !l_iter_end(&it); l_iter_next(&it)) + { + // Get a reference to the current value + int *r = l_iter_rvalue(&it); + + if (r != NULL && *r % 2 == 0) + *r = 7; + } + + // Now print it to see the difference + for (list_iter it = my_list->it_start(my_list); !l_iter_end(&it); l_iter_next(&it)) + { + int e = l_iter_value(&it); + size_t i = l_iter_index(&it); + + if (i == 0) + printf("[ %d, ", e); + else if (i == l_count(my_list) - 1) + printf("%d ]\n", e); + else + printf("%d, ", e); + } + + // Dispose the iterator called by l_iter_new() + l_iter_free(iter); + // Dispose the list created + l_free(my_list); + + return 0; +} diff --git a/examples/list/list.c b/examples/list/list.c index 0857886..c3ff70e 100644 --- a/examples/list/list.c +++ b/examples/list/list.c @@ -7,8 +7,8 @@ * Leonardo Vencovsky (https://github.com/LeoVen) * */ -#include #include "list.h" +#include LIST_GENERATE(l, list, /* FMOD */, int) @@ -44,7 +44,9 @@ int main(int argc, char const *argv[]) // Add even numbers to a given index. In this case they are being added // to the end of the list (count index == push_back). With a condition // of being even (i % 2 == 0) - l_push_if(my_list, i, l_count(my_list), i % 2 == 0); + bool added = l_push_if(my_list, i, l_count(my_list), i % 2 == 0); + + // If added is true, the element was added to the list } // Iterator @@ -54,12 +56,13 @@ int main(int argc, char const *argv[]) // Iterate over each element starting from the start (iter_new positions // the iterator to the start of the list) until the end of the list. - for (l_iter_init(&iter, my_list); !l_iter_end(&iter);) + for (l_iter_init(&iter, my_list); !l_iter_end(&iter); l_iter_next(&iter)) { // Here index corresponds to the position of the result relative to all // other elements in the iteration. It is only a coincidence that the // index here is the same as the result's position in the list. - l_iter_next(&iter, &result, &index); + index = l_iter_index(&iter); + result = l_iter_value(&iter); // Do whatever you want with result and index, e.g. sum += result * index; diff --git a/examples/list/string.c b/examples/list/string.c index 4408e3d..8708582 100644 --- a/examples/list/string.c +++ b/examples/list/string.c @@ -21,7 +21,7 @@ int chrcmp(char a, char b) void print_str(string *str) { - // If the buffer is not completely filled, there will be at leas one null + // If the buffer is not completely filled, there will be at least one null // byte in the end of the list's contents. This printf is possible but very // dangerous as you might end up printing something else from memory until // a null byte. @@ -57,19 +57,18 @@ int main(int argc, char const *argv[]) size_t index; char result; - for (str_iter_tostart(&iter); !str_iter_end(&iter);) + // Printing each character + for (str_iter_to_start(&iter); !str_iter_end(&iter); str_iter_next(&iter)) { - str_iter_next(&iter, &result, &index); - putchar(result); + putchar(str_iter_value(&iter)); } printf("\n\n"); // You can also print it reversed - for (str_iter_toend(&iter); !str_iter_start(&iter);) + for (str_iter_to_end(&iter); !str_iter_start(&iter); str_iter_prev(&iter)) { - str_iter_prev(&iter, &result, &index); - putchar(result); + putchar(str_iter_value(&iter)); } printf("\n\n"); @@ -77,15 +76,20 @@ int main(int argc, char const *argv[]) // Now remove the first letter supposing we don't know where it starts index = str_indexof(my_str, ' ', chrcmp, true); + if (index == str_count(my_str)) + { + // char not found... + // Treat error + } + // Now that we know where the first word is, remove it, including the // extra space. [from, to] are inclusive. str_remove(my_str, 0, index); // Print it once again - for (str_iter_tostart(&iter); !str_iter_end(&iter);) + for (str_iter_to_start(&iter); !str_iter_end(&iter); str_iter_next(&iter)) { - str_iter_next(&iter, &result, &index); - putchar(result); + putchar(str_iter_value(&iter)); } printf("\nString length: %lu\n\n", str_count(my_str)); @@ -106,20 +110,22 @@ int main(int argc, char const *argv[]) // Pop white space str_pop_front(str_out); - for (str_iter_init(&iter, my_str); !str_iter_end(&iter);) + for (str_iter_init(&iter, my_str); !str_iter_end(&iter); str_iter_next(&iter)) { - str_iter_next(&iter, &result, &index); - putchar(result); + putchar(str_iter_value(&iter)); } + printf("\nString length: %lu\n\n", str_count(my_str)); - for (str_iter_init(&iter, str_out); !str_iter_end(&iter);) + + for (str_iter_init(&iter, str_out); !str_iter_end(&iter); str_iter_next(&iter)) { - str_iter_next(&iter, &result, &index); - putchar(result); + putchar(str_iter_value(&iter)); } + printf("\nString length: %lu\n", str_count(str_out)); } + // Free strings str_free(my_str); str_free(str_out); diff --git a/src/list.h b/src/list.h index 1bb5617..e36ef0e 100644 --- a/src/list.h +++ b/src/list.h @@ -37,76 +37,112 @@ #define LIST_GENERATE_SOURCE_PUBLIC(PFX, SNAME, FMOD, K, V) \ LIST_GENERATE_SOURCE(PFX, SNAME, FMOD, V) /* STRUCT ********************************************************************/ -#define LIST_GENERATE_STRUCT(PFX, SNAME, FMOD, V) \ - \ - struct SNAME##_s \ - { \ - V *buffer; \ - size_t capacity; \ - size_t count; \ - }; \ - \ - struct SNAME##_iter_s \ - { \ - struct SNAME##_s *target; \ - size_t cursor; \ - bool start; \ - bool end; \ - }; \ - \ +#define LIST_GENERATE_STRUCT(PFX, SNAME, FMOD, V) + /* HEADER ********************************************************************/ #define LIST_GENERATE_HEADER(PFX, SNAME, FMOD, V) \ \ - typedef struct SNAME##_s SNAME; \ - typedef struct SNAME##_iter_s SNAME##_iter; \ + typedef struct SNAME##_s \ + { \ + /* Dynamic array of elements*/ \ + V *buffer; \ \ - FMOD SNAME *PFX##_new(size_t size); \ + /* Current array capacity */ \ + size_t capacity; \ + \ + /* Current amount of elements */ \ + size_t count; \ + \ + /* Function that returns an iterator to the start of the list */ \ + struct SNAME##_iter_s (*it_start)(struct SNAME##_s *); \ + \ + /* Function that returns an iterator to the end of the list */ \ + struct SNAME##_iter_s (*it_end)(struct SNAME##_s *); \ + \ + } SNAME, *SNAME##_ptr; \ + \ + typedef struct SNAME##_iter_s \ + { \ + /* Target list */ \ + struct SNAME##_s *target; \ + \ + /* Cursor's position */ \ + size_t cursor; \ + \ + /* 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 capacity); \ FMOD SNAME *PFX##_new_from(V *elements, size_t size); \ FMOD void PFX##_clear(SNAME *_list_); \ FMOD void PFX##_free(SNAME *_list_); \ + /* Collection Input and Output */ \ FMOD bool PFX##_push_front(SNAME *_list_, V element); \ FMOD bool PFX##_push(SNAME *_list_, V element, size_t index); \ FMOD bool PFX##_push_back(SNAME *_list_, V element); \ FMOD bool PFX##_pop_front(SNAME *_list_); \ FMOD bool PFX##_pop(SNAME *_list_, size_t index); \ FMOD bool PFX##_pop_back(SNAME *_list_); \ + /* Conditional Input and Output */ \ FMOD bool PFX##_push_if(SNAME *_list_, V element, size_t index, bool condition); \ FMOD bool PFX##_pop_if(SNAME *_list_, size_t index, bool condition); \ + /* Collection Range Input and Output */ \ FMOD bool PFX##_prepend(SNAME *_list_, V *elements, size_t size); \ FMOD bool PFX##_insert(SNAME *_list_, V *elements, size_t size, size_t index); \ FMOD bool PFX##_append(SNAME *_list_, V *elements, size_t size); \ FMOD bool PFX##_remove(SNAME *_list_, size_t from, size_t to); \ FMOD SNAME *PFX##_extract(SNAME *_list_, size_t from, size_t to); \ + /* Element Access */ \ FMOD V PFX##_front(SNAME *_list_); \ FMOD V PFX##_get(SNAME *_list_, size_t index); \ FMOD V *PFX##_get_ref(SNAME *_list_, size_t index); \ FMOD V PFX##_back(SNAME *_list_); \ FMOD size_t PFX##_indexof(SNAME *_list_, V element, int (*comparator)(V, V), bool from_start); \ FMOD bool PFX##_contains(SNAME *_list_, V element, int (*comparator)(V, V)); \ + /* Collection State */ \ FMOD bool PFX##_empty(SNAME *_list_); \ FMOD bool PFX##_full(SNAME *_list_); \ FMOD size_t PFX##_count(SNAME *_list_); \ FMOD bool PFX##_fits(SNAME *_list_, size_t size); \ FMOD size_t PFX##_capacity(SNAME *_list_); \ \ + /* 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 LIST_GENERATE_SOURCE(PFX, SNAME, FMOD, V) \ \ - FMOD bool PFX##_grow(SNAME *_list_, size_t required); \ + /* Implementation Detail Functions */ \ + FMOD bool PFX##_impl_grow(SNAME *_list_, size_t required); \ + SNAME##_iter PFX##_impl_it_start(SNAME *_list_); \ + SNAME##_iter PFX##_impl_it_end(SNAME *_list_); \ \ - FMOD SNAME *PFX##_new(size_t size) \ + FMOD SNAME *PFX##_new(size_t capacity) \ { \ - if (size < 1) \ + if (capacity < 1) \ return NULL; \ \ SNAME *_list_ = malloc(sizeof(SNAME)); \ @@ -114,7 +150,7 @@ if (!_list_) \ return NULL; \ \ - _list_->buffer = malloc(size * sizeof(V)); \ + _list_->buffer = malloc(capacity * sizeof(V)); \ \ if (!_list_->buffer) \ { \ @@ -122,11 +158,14 @@ return NULL; \ } \ \ - memset(_list_->buffer, 0, size * sizeof(V)); \ + memset(_list_->buffer, 0, capacity * sizeof(V)); \ \ - _list_->capacity = size; \ + _list_->capacity = capacity; \ _list_->count = 0; \ \ + _list_->it_start = PFX##_impl_it_start; \ + _list_->it_end = PFX##_impl_it_end; \ + \ return _list_; \ } \ \ @@ -164,7 +203,7 @@ { \ if (PFX##_full(_list_)) \ { \ - if (!PFX##_grow(_list_, _list_->count + 1)) \ + if (!PFX##_impl_grow(_list_, _list_->count + 1)) \ return false; \ } \ \ @@ -196,7 +235,7 @@ \ if (PFX##_full(_list_)) \ { \ - if (!PFX##_grow(_list_, _list_->count + 1)) \ + if (!PFX##_impl_grow(_list_, _list_->count + 1)) \ return false; \ } \ \ @@ -213,7 +252,7 @@ { \ if (PFX##_full(_list_)) \ { \ - if (!PFX##_grow(_list_, _list_->count + 1)) \ + if (!PFX##_impl_grow(_list_, _list_->count + 1)) \ return false; \ } \ \ @@ -291,7 +330,7 @@ \ if (!PFX##_fits(_list_, size)) \ { \ - if (!PFX##_grow(_list_, _list_->count + size)) \ + if (!PFX##_impl_grow(_list_, _list_->count + size)) \ return false; \ } \ \ @@ -317,7 +356,7 @@ { \ if (!PFX##_fits(_list_, size)) \ { \ - if (!PFX##_grow(_list_, _list_->count + size)) \ + if (!PFX##_impl_grow(_list_, _list_->count + size)) \ return false; \ } \ \ @@ -338,7 +377,7 @@ \ if (!PFX##_fits(_list_, size)) \ { \ - if (!PFX##_grow(_list_, _list_->count + size)) \ + if (!PFX##_impl_grow(_list_, _list_->count + size)) \ return false; \ } \ \ @@ -517,27 +556,25 @@ 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 = 0; \ iter->start = true; \ iter->end = PFX##_empty(iter->target); \ } \ \ - FMOD void PFX##_iter_toend(SNAME##_iter *iter) \ + FMOD void PFX##_iter_to_end(SNAME##_iter *iter) \ { \ - iter->cursor = iter->target->count - 1; \ iter->start = PFX##_empty(iter->target); \ + iter->cursor = PFX##_empty(iter->target) ? 0 : iter->target->count - 1; \ 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->cursor; \ - *result = iter->target->buffer[iter->cursor]; \ iter->start = false; \ \ if (iter->cursor == iter->target->count - 1) \ @@ -548,13 +585,11 @@ 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->cursor; \ - *result = iter->target->buffer[iter->cursor]; \ iter->end = false; \ \ if (iter->cursor == 0) \ @@ -565,7 +600,28 @@ return true; \ } \ \ - FMOD bool PFX##_grow(SNAME *_list_, size_t required) \ + 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->cursor; \ + } \ + \ + FMOD bool PFX##_impl_grow(SNAME *_list_, size_t required) \ { \ size_t new_capacity = _list_->capacity * 2; \ \ @@ -581,6 +637,26 @@ _list_->capacity = new_capacity; \ \ 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_LIST_H */