From 3fbe8ec0b60f9a870903ace8e6243ebd0cb5b0a3 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Thu, 29 May 2025 10:41:20 +0200 Subject: [PATCH] Update _transform, _reduce and _map_reduce to be more flexible Rename _map_reduce to _transform_reduce --- README.md | 37 +++++++++++++++++++++++-------------- doc/API.txt | 3 +++ m-algo.h | 39 +++++++++++++++++++++------------------ tests/test-malgo.c | 44 +++++++++++++++++++++++++++++++++++++++----- 4 files changed, 86 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index c2e45388..1e3587c0 100644 --- a/README.md +++ b/README.md @@ -8200,31 +8200,40 @@ Apply the function `func` to each element of the container `c`. The function may transform the element provided it doesn't reallocate the object and if the container supports iterating over modifiable elements. -##### `void name_transform(container_t d, container_t c, void (*func)(type_t *out, const type_t in))` +##### `void name_transform(container_t d, container_t c, bool (*func)(type_t *out, const type_t in, void *data), void *data)` -Apply the function `func` to each element of the container `c` -and push the result into the container `d` so that `d = func(c)` +Reset the container `d`, then +apply the function `func` to each element of the container `c` +and push its output `*out` into the container `d` if its return value is true. -`func` shall output in the initialized object `out` -the transformed value of the constant object `in`. -Afterwards `out` is pushed moved into `d`. +`func` shall output in the initialized object `*out` +the transformed value of the constant object `in` if it decides this value is kept (its return value is true in this case). +Otherwise its return value is false. +`data` is a user context data pointer free to be used and passed to `func` -This method is defined only if the base type exports an `INIT` method. -This method is defined only if the container exports a `PUSH_MOVE` method. -`c` and `d` cannot be the same containers. +This method is defined only if the base type exports an `INIT` method and if the container exports a `PUSH` method. +`c` and `d` shall not be the same containers. -##### `void name_reduce(type_t *dest, const container_t c, void (*func)(type_t *, type_t const))` +##### `bool name_reduce(type_t *dest, const container_t c, void (*func)(type_t *, type_t const))` Perform a reduction using the function `func` to the elements of the container `c`. The final result is stored in `*dest`. -If there is no element, `*dest` is let unmodified. +If there is no element, `*dest` is let unmodified and it returns `false`. +Otherwise it returns `true` and `*dest` is modified. -##### `void name_map_reduce(type_t *dest, const container_t c, void (*redFunc)(type_t *, type_t const), void *(mapFunc)(type_t *, type_t const))` +##### `bool name_transform_reduce(type_t *dest, const container_t c, void (*redFunc)(type_t *, type_t const), bool *(mapFunc)(type_t *, type_t const, void *data), void *data)` Perform a reduction using the function `redFunc` -to the transformed elements of the container `c` using `mapFunc`. +to the transformed and kept elements of the container `c` using `mapFunc`. The final result is stored in `*dest`. -If there is no element, `*dest` is let unmodified. + +`func` shall output in the initialized object `*out` +the transformed value of the constant object `in` if it decides this value is kept (its return value is true in this case). +Otherwise its return value is false. +`data` is a user context data pointer free to be used and passed to `func` + +If there is no element, `*dest` is let unmodified and it returns `false`. +Otherwise it returns `true` and `*dest` is modified. ##### `bool name_any_of_p(const container_t c, void *(func)(const type_t))` diff --git a/doc/API.txt b/doc/API.txt index 15c24931..1908d447 100644 --- a/doc/API.txt +++ b/doc/API.txt @@ -108,3 +108,6 @@ V0.8.0 : * Memory interface massive compatibility break to allow more efficient (It's because supporting emulation layer with new memory context parameter was complicated) * Remove obsole headers: m-c-mempool, m-mempool, m-shared, m-i-shared, -mconcurrent +V0.8.1 : * m-algo: Update interface of _transform to be more flexible: adding user context pointer, and filtering of the input elements. + * m-algo: Update interface of _reduce to be more user friendly: return value indicates if the reduction is empty or not. + * m-algo: Rename _map_reduce into _transform_reduce and update its interface to be more flexible: adding user context pointer, and filtering of the input elements and return value indicates if the reduction is empty or not. diff --git a/m-algo.h b/m-algo.h index 1b0dc7dd..2c6024ce 100644 --- a/m-algo.h +++ b/m-algo.h @@ -170,7 +170,8 @@ typedef bool (*M_F(name, _test_cb_ct))(type_t const); \ typedef bool (*M_F(name, _eq_cb_ct))(type_t const, type_t const); \ typedef int (*M_F(name, _cmp_cb_ct))(type_t const, type_t const); \ - typedef void (*M_F(name, _transform_cb_ct))(type_t *, type_t const); \ + typedef bool (*M_F(name, _transform_cb_ct))(type_t *, type_t const, void*); \ + typedef void (*M_F(name, _reduce_cb_ct))(type_t *, type_t const); \ typedef void (*M_F(name, _apply_cb_ct))(type_t M_IF_METHOD(IT_REF, cont_oplist)( , const) ); @@ -181,7 +182,7 @@ M_FUNC_OBJ_ITF_DEF(M_F(name, _test_obj), bool, type_t const) \ M_FUNC_OBJ_ITF_DEF(M_F(name, _eq_obj), bool, type_t const, type_t const ) \ M_FUNC_OBJ_ITF_DEF(M_F(name, _cmp_obj), int, type_t const, type_t const ) \ - M_FUNC_OBJ_ITF_DEF(M_F(name, _transform_obj), void, type_t *, type_t const )\ + M_FUNC_OBJ_ITF_DEF(M_F(name, _transform_obj), bool, type_t *, type_t const )\ M_FUNC_OBJ_ITF_DEF(M_F(name, _apply_obj), void, type_t * ) \ @@ -756,27 +757,28 @@ } \ \ M_IF_METHOD(INIT, type_oplist)( \ - M_IF_METHOD(PUSH_MOVE, cont_oplist)( \ + M_IF_METHOD(PUSH, cont_oplist)( \ \ /* Apply func for all elements of the container src and push the result in dst */ \ - M_P(void, name, _transform, container_t dst, container_t src, M_F(name, _transform_cb_ct) func) \ + M_P(void, name, _transform, container_t dst, container_t src, M_F(name, _transform_cb_ct) func, void *data) \ { \ M_ASSERT(dst != src); \ M_CALL_RESET(cont_oplist, dst); \ + type_t tmp; \ + M_CALL_INIT(type_oplist, tmp); \ for M_EACH(item, src, cont_oplist) { \ - type_t tmp; \ - M_CALL_INIT(type_oplist, tmp); \ - func(&tmp, *item); \ - M_CALL_PUSH_MOVE(cont_oplist, dst, &tmp); \ + if (func(&tmp, *item, data)) { \ + M_CALL_PUSH(cont_oplist, dst, tmp); \ + } \ } \ + M_CALL_CLEAR(type_oplist, tmp); \ M_IF_METHOD(REVERSE, cont_oplist)(M_CALL_REVERSE(cont_oplist, dst),); \ } \ , /* END PUSH_MOVE */), /* END INIT */ ) \ \ /* Reduce all elements of the container in dst in function of func */ \ M_IF_METHOD(SET, type_oplist)( \ - M_P(void, name, _reduce, type_t *dest, container_t const l, \ - M_F(name, _transform_cb_ct) func) \ + M_P(bool, name, _reduce, type_t *dest, container_t const l, M_F(name, _reduce_cb_ct) func) \ { \ M_UNUSED_CONTEXT(); \ bool initDone = false; \ @@ -788,15 +790,15 @@ initDone = true; \ } \ } \ + return initDone; \ } \ , /* END SET */) \ \ /* Reduce all transformed elements of the container in dst in function of func */ \ M_IF_METHOD(INIT, type_oplist)( \ - M_P(void, name, _map_reduce, type_t *dest, \ - const container_t l, \ - M_F(name, _transform_cb_ct) redFunc, \ - M_F(name, _transform_cb_ct) mapFunc) \ + M_P(bool, name, _transform_reduce, type_t *dest, const container_t l, \ + M_F(name, _reduce_cb_ct) redFunc, \ + M_F(name, _transform_cb_ct) mapFunc, void *data) \ { \ M_UNUSED_CONTEXT(); \ bool initDone = false; \ @@ -804,14 +806,15 @@ M_CALL_INIT(type_oplist, tmp); \ for M_EACH(item, l, cont_oplist) { \ if (initDone) { \ - mapFunc(&tmp, *item); \ - redFunc(dest, tmp); \ + if (mapFunc(&tmp, *item, data) ) { \ + redFunc(dest, tmp); \ + } \ } else { \ - mapFunc(dest, *item); \ - initDone = true; \ + initDone = mapFunc(dest, *item, data); \ } \ } \ M_CALL_CLEAR(type_oplist, tmp); \ + return initDone; \ } \ , ) \ diff --git a/tests/test-malgo.c b/tests/test-malgo.c index 32e65241..994cab5e 100644 --- a/tests/test-malgo.c +++ b/tests/test-malgo.c @@ -82,11 +82,21 @@ static void g_f(int n) g_count++; } -static void func_map(int *d, int n) +static bool func_map(int *d, int n, void *data) { assert (g_min <= n && n <= g_max); g_count++; *d = n * n; + return (data == NULL) || (n != 0); +} + +static bool func_map_none(int *d, int n, void *data) +{ + (void) d; + (void) n; + (void) data; + g_count++; + return false; } static void func_reduce(int *d, int n) @@ -156,13 +166,17 @@ static void test_list(void) g_min = 0; g_max = 99; g_count = 0; - algo_list_transform(tmp, l, func_map); + algo_list_transform(tmp, l, func_map, NULL); assert(g_count == 101); assert(list_int_size(tmp) == 101); for(int i = 0; i < 100; i++) { assert(*list_int_get(tmp, (size_t) i) == i * i); } assert(*list_int_get(tmp, 100) == 17 * 17); + g_count = 0; + algo_list_transform(tmp, l, func_map, (void*)&g_count); + assert(g_count == 101); + assert(list_int_size(tmp) == 100); } int *p = algo_list_min(l); @@ -305,19 +319,35 @@ static void test_array(void) assert(g_count == 101); int n; - algo_array_reduce(&n, l, func_reduce); + bool b; + b = algo_array_reduce(&n, l, func_reduce); + assert(b); assert(n == 100*99/2+17); g_min = 0; g_max = 99; g_count = 0; - algo_array_map_reduce(&n, l, func_reduce, func_map); + b = algo_array_transform_reduce(&n, l, func_reduce, func_map, NULL); + assert(b); assert(g_count == 101); assert(n == (328350 + 17*17)); + g_count = 0; + b = algo_array_transform_reduce(&n, l, func_reduce, func_map, (void*) &g_count); + assert(b); + assert(g_count == 101); + assert(n == (328350 + 17*17)); + + g_count = 0; + n = -1; + b = algo_array_transform_reduce(&n, l, func_reduce, func_map_none, NULL); + assert(!b); + assert(g_count == 101); + assert(n == -1); + M_LET(tmp, array_int_t) { g_count = 0; - algo_array_transform(tmp, l, func_map); + algo_array_transform(tmp, l, func_map, NULL); assert(g_count == 101); assert(array_int_size(tmp) == 101); for(int i = 0; i < 100; i++) { @@ -326,6 +356,10 @@ static void test_array(void) } assert(*array_int_get(l, 100) == 17); assert(*array_int_get(tmp, 100) == 17 * 17); + g_count = 0; + algo_array_transform(tmp, l, func_map, (void*) &g_count); + assert(g_count == 101); + assert(array_int_size(tmp) == 100); array_int_reset(tmp); #define FT(d,x) ((d) = (x) + 1)