diff --git a/README.md b/README.md index a0e8efd..14014ac 100644 --- a/README.md +++ b/README.md @@ -24,63 +24,11 @@ At its simplest, you can use this library as follows: #include #include "src/vector.h" -/* - * Compile with: gcc main.c src/vector.c - * Output: First element: 1 - * Head of vector: 16, size is now: 1 - */ +vector_order_t cmp_asc(const void *a, const void *b) { + const int x = *(int *)a, y = *(int *)b; -// Callback functions -vector_order_t cmp_int_asc(const void *x, const void *y); -void square(void *element, void *env); -int is_even(const void *element, void *env); -void add(void *accumulator, const void *element, void *env); - -int main(void) { - // Create an integer vector of initial capacity equal to 5 - vector_t *vec = vector_new(5, sizeof(int)).value.vector; - - // Add some elements - vector_push(vec, &(int){1}); // Equivalent as below - int nums[] = {5, 2, 4, 3}; - for (int idx = 0; idx < 4; idx++) { vector_push(vec, &nums[idx]); } - - // Sort array in ascending order: [1, 2, 3, 4, 5] - vector_sort(vec, cmp_int_asc); - - // Print 1st element - const int first = *(int*)vector_get(vec, 0).value.element; - printf("First element: %d\n", first); - - int sum = 0; - vector_map(vec, square, NULL); // Square elements: [1, 2, 3, 4, 5] -> [1, 4, 9, 16, 25] - vector_filter(vec, is_even, NULL); // Filter even elements: [1, 4, 9, 16, 25] -> [4, 16] - vector_reduce(vec, &sum, add, NULL); // Sum elements: [4, 16] -> 20 - - // Pop second element using LIFO policy - const int head = *(int*)vector_pop(vec).value.element; - printf("Head of vector: %d, size is now: %zu\n", head, vector_size(vec)); - - // Remove vector from memory - vector_destroy(vec); - - return 0; -} - -vector_order_t cmp_int_asc(const void *x, const void *y) { - int x_int = *(const int*)x; - int y_int = *(const int*)y; - - if (x_int < y_int) return VECTOR_ORDER_LT; - if (x_int > y_int) return VECTOR_ORDER_GT; - - return VECTOR_ORDER_EQ; -} - -void square(void *element, void *env) { - (void)(env); - int *value = (int*)element; - *value = (*value) * (*value); + if (x < y) return VECTOR_ORDER_LT; + return (x > y) ? VECTOR_ORDER_GT : VECTOR_ORDER_EQ; } int is_even(const void *element, void *env) { @@ -90,9 +38,25 @@ int is_even(const void *element, void *env) { return (value % 2) == 0; } -void add(void *accumulator, const void *element, void *env) { - (void)(env); - *(int*)accumulator += *(int*)element; +/* Compile with: gcc main.c src/vector.c + * Output: '2 4' + */ +int main(void) { + vector_t *vec = vector_new(5, sizeof(int)).value.vector; // Create a vector of integers + + int nums[] = {5, 4, 1, 2, 3}; // Push some elements + for (int idx = 0; idx < 5; idx++) { vector_push(vec, &nums[idx]); } + + vector_sort(vec, cmp_asc); // Sort vector + vector_filter(vec, is_even, NULL); // Filter even elements + + for (int idx = 0; idx < 2; idx++) { + printf("%d ", *(int *)vector_get(vec, idx).value.element); + } + putchar('\n'); + + vector_destroy(vec); // Remove vector from memory + return 0; } ``` @@ -177,7 +141,7 @@ int main(void) { #include "src/string.h" /* - * Compile with: gcc -O3 main.c src/string.c + * Compile with: gcc main.c src/string.c * Output: Final string: "Hello,World,😀" Splitted: ["Hello" "World" "😀" ] */ int main(void) { @@ -219,8 +183,7 @@ This will compile the library as well as the `usage.c` file, the unit tests and > GNU Multiple Precision Arithmetic Library (GMP). ## Documentation -For additional details about this library (internal design, memory -management, data ownership, etc.) go to the [docs folder](/docs). +For additional details about this library (internal design, memory management, data ownership, etc.) go to the [docs folder](/docs). ## Unit tests Datum provides some unit tests for `Vector`, `Map` and `BigInt`. To run them, you can issue the following commands: diff --git a/docs/vector.md b/docs/vector.md index dd67d82..d10a196 100644 --- a/docs/vector.md +++ b/docs/vector.md @@ -29,7 +29,7 @@ At the time being, `Vector` supports the following methods: - `vector_result_t vector_push(vector, value)`: add a new value to the vector; - `vector_result_t vector_set(vector, index, value)`: update the value of a given index if it exists; - `vector_result_t vector_get(vector, index)`: return the value indexed by `index` if it exists; -- `map_result_t vector_sort(map, cmp)`: sort array using `cmp` function; +- `vector_result_t vector_sort(vector, cmp)`: sort vector using `cmp` function; - `vector_result_t vector_pop(vector)`: pop last element from the vector following the LIFO policy; - `vector_result_t vector_map(vector, callback, env)`: apply `callback` function to vector (in-place); - `vector_result_t vector_filter(vector, callback, env)`: filter vector using `callback` (in-place); @@ -85,14 +85,80 @@ In particular, you should be aware of the following design choices: - The `vector_reduce` callback method requires the caller to initialize an _"accumulator"_ variable before calling this method; - The `vector_filter` callback method is expected to return non-zero to keep the element and zero to filter it out. - The `env` argument is an optional parameter to pass the external environment to the callback function. It is used to mock the behavior of closures, where -the lexical environment is captured when the closure is created. +the lexical environment is captured when the closure is created; +- Callback functions must be self-contained and handle all their resources. Additionally, they are responsible for ensuring their operations +don't cause any undefined behavior. + +Let's look at an example: + +```c +#include +#include "src/vector.h" + +// Callback functions +void square(void *element, void *env); +int is_even(const void *element, void *env); +void add(void *accumulator, const void *element, void *env); + +int main(void) { + // Create an integer vector of initial capacity equal to 5 + vector_t *vec = vector_new(5, sizeof(int)).value.vector; + + int nums[] = {1, 2, 3, 4, 5}; + for (int idx = 0; idx < 5; idx++) { + vector_push(vec, &nums[idx]); + } + + // Square elements: [1, 2, 3, 4, 5] -> [1, 4, 9, 16, 25] + vector_map(vec, square, NULL); + for (int idx = 0; idx < 5; idx++) { + printf("%d ", *(int *)vector_get(vec, idx).value.element); + } + putchar('\n'); + + // Filter even elements: [1, 4, 9, 16, 25] -> [4, 16] + vector_filter(vec, is_even, NULL); + for (int idx = 0; idx < 2; idx++) { + printf("%d ", *(int *)vector_get(vec, idx).value.element); + } + putchar('\n'); + + // Sum elements: [4, 16] -> 20 + int sum = 0; + vector_reduce(vec, &sum, add, NULL); + printf("%d\n", sum); + + vector_destroy(vec); + + return 0; +} + +void square(void *element, void *env) { + (void)(env); + int *value = (int*)element; + *value = (*value) * (*value); +} + +int is_even(const void *element, void *env) { + (void)(env); + int value = *(int*)element; + + return (value % 2) == 0; +} + +void add(void *accumulator, const void *element, void *env) { + (void)(env); + *(int*)accumulator += *(int*)element; +} +``` ## Sorting As indicated in the [its documentation](/docs/vector.md), the `Vector` data type provides an efficient in-place sorting function called `vector_sort` that uses a builtin implementation of the [Quicksort algorithm](https://en.wikipedia.org/wiki/Quicksort). This method requires an user-defined comparison procedure which allows the -caller to customize the sorting behavior. The comparison procedure must adhere to the -following specification: +caller to customize the sorting behavior. + +The comparison procedure must adhere to the following specification: 1. Must return `vector_order_t`, which is defined as follows: @@ -107,7 +173,7 @@ typedef enum { and indicates the ordering relationship between any two elements. 2. Must accept two `const void*` parameters representing two elements to compare; -3. Must be self-contained and handle all its own resources. +3. Must be self-contained and handle all its resources. Additionally, it's responsible for ensuring its operations don't cause any undefined behavior. Let's look at some examples. For instance, let's say that we want to sort an array of integers in ascending and descending order: @@ -117,8 +183,8 @@ of integers in ascending and descending order: #include "src/vector.h" vector_order_t cmp_int_asc(const void *x, const void *y) { - int x_int = *(const int*)x; - int y_int = *(const int*)y; + const int x_int = *(const int*)x; + const int y_int = *(const int*)y; if (x_int < y_int) return VECTOR_ORDER_LT; if (x_int > y_int) return VECTOR_ORDER_GT;