Update _transform, _reduce and _map_reduce to be more flexible

Rename _map_reduce to _transform_reduce
This commit is contained in:
Patrick Pelissier committed 2025-05-29 10:41:20 +02:00
1 parent 34085aa637
commit 3fbe8ec0b6
4 files changed
+86 -37

No files matched your search

+23 -14
View File
@@ -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 The function may transform the element provided it doesn't reallocate the
object and if the container supports iterating over modifiable elements. 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` Reset the container `d`, then
and push the result into the container `d` so that `d = func(c)` 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` `func` shall output in the initialized object `*out`
the transformed value of the constant object `in`. the transformed value of the constant object `in` if it decides this value is kept (its return value is true in this case).
Afterwards `out` is pushed moved into `d`. 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 base type exports an `INIT` method and if the container exports a `PUSH` method.
This method is defined only if the container exports a `PUSH_MOVE` method. `c` and `d` shall not be the same containers.
`c` and `d` cannot 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`. Perform a reduction using the function `func` to the elements of the container `c`.
The final result is stored in `*dest`. 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` 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`. 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))` ##### `bool name_any_of_p(const container_t c, void *(func)(const type_t))`
+3
View File
@@ -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) (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 * 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.
+21 -18
View File
@@ -170,7 +170,8 @@
typedef bool (*M_F(name, _test_cb_ct))(type_t const); \ 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 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 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) ); 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, _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, _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, _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 * ) \ 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(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 */ \ /* 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_ASSERT(dst != src); \
M_CALL_RESET(cont_oplist, dst); \ M_CALL_RESET(cont_oplist, dst); \
type_t tmp; \
M_CALL_INIT(type_oplist, tmp); \
for M_EACH(item, src, cont_oplist) { \ for M_EACH(item, src, cont_oplist) { \
type_t tmp; \ if (func(&tmp, *item, data)) { \
M_CALL_INIT(type_oplist, tmp); \ M_CALL_PUSH(cont_oplist, dst, tmp); \
func(&tmp, *item); \ } \
M_CALL_PUSH_MOVE(cont_oplist, dst, &tmp); \
} \ } \
M_CALL_CLEAR(type_oplist, tmp); \
M_IF_METHOD(REVERSE, cont_oplist)(M_CALL_REVERSE(cont_oplist, dst),); \ M_IF_METHOD(REVERSE, cont_oplist)(M_CALL_REVERSE(cont_oplist, dst),); \
} \ } \
, /* END PUSH_MOVE */), /* END INIT */ ) \ , /* END PUSH_MOVE */), /* END INIT */ ) \
\ \
/* Reduce all elements of the container in dst in function of func */ \ /* Reduce all elements of the container in dst in function of func */ \
M_IF_METHOD(SET, type_oplist)( \ M_IF_METHOD(SET, type_oplist)( \
M_P(void, name, _reduce, type_t *dest, container_t const l, \ M_P(bool, name, _reduce, type_t *dest, container_t const l, M_F(name, _reduce_cb_ct) func) \
M_F(name, _transform_cb_ct) func) \
{ \ { \
M_UNUSED_CONTEXT(); \ M_UNUSED_CONTEXT(); \
bool initDone = false; \ bool initDone = false; \
@@ -788,15 +790,15 @@
initDone = true; \ initDone = true; \
} \ } \
} \ } \
return initDone; \
} \ } \
, /* END SET */) \ , /* END SET */) \
\ \
/* Reduce all transformed elements of the container in dst in function of func */ \ /* Reduce all transformed elements of the container in dst in function of func */ \
M_IF_METHOD(INIT, type_oplist)( \ M_IF_METHOD(INIT, type_oplist)( \
M_P(void, name, _map_reduce, type_t *dest, \ M_P(bool, name, _transform_reduce, type_t *dest, const container_t l, \
const container_t l, \ M_F(name, _reduce_cb_ct) redFunc, \
M_F(name, _transform_cb_ct) redFunc, \ M_F(name, _transform_cb_ct) mapFunc, void *data) \
M_F(name, _transform_cb_ct) mapFunc) \
{ \ { \
M_UNUSED_CONTEXT(); \ M_UNUSED_CONTEXT(); \
bool initDone = false; \ bool initDone = false; \
@@ -804,14 +806,15 @@
M_CALL_INIT(type_oplist, tmp); \ M_CALL_INIT(type_oplist, tmp); \
for M_EACH(item, l, cont_oplist) { \ for M_EACH(item, l, cont_oplist) { \
if (initDone) { \ if (initDone) { \
mapFunc(&tmp, *item); \ if (mapFunc(&tmp, *item, data) ) { \
redFunc(dest, tmp); \ redFunc(dest, tmp); \
} \
} else { \ } else { \
mapFunc(dest, *item); \ initDone = mapFunc(dest, *item, data); \
initDone = true; \
} \ } \
} \ } \
M_CALL_CLEAR(type_oplist, tmp); \ M_CALL_CLEAR(type_oplist, tmp); \
return initDone; \
} \ } \
, ) \ , ) \
+39 -5
View File
@@ -82,11 +82,21 @@ static void g_f(int n)
g_count++; 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); assert (g_min <= n && n <= g_max);
g_count++; g_count++;
*d = n * n; *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) static void func_reduce(int *d, int n)
@@ -156,13 +166,17 @@ static void test_list(void)
g_min = 0; g_min = 0;
g_max = 99; g_max = 99;
g_count = 0; g_count = 0;
algo_list_transform(tmp, l, func_map); algo_list_transform(tmp, l, func_map, NULL);
assert(g_count == 101); assert(g_count == 101);
assert(list_int_size(tmp) == 101); assert(list_int_size(tmp) == 101);
for(int i = 0; i < 100; i++) { for(int i = 0; i < 100; i++) {
assert(*list_int_get(tmp, (size_t) i) == i * i); assert(*list_int_get(tmp, (size_t) i) == i * i);
} }
assert(*list_int_get(tmp, 100) == 17 * 17); 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); int *p = algo_list_min(l);
@@ -305,19 +319,35 @@ static void test_array(void)
assert(g_count == 101); assert(g_count == 101);
int n; 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); assert(n == 100*99/2+17);
g_min = 0; g_min = 0;
g_max = 99; g_max = 99;
g_count = 0; 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(g_count == 101);
assert(n == (328350 + 17*17)); 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) { M_LET(tmp, array_int_t) {
g_count = 0; g_count = 0;
algo_array_transform(tmp, l, func_map); algo_array_transform(tmp, l, func_map, NULL);
assert(g_count == 101); assert(g_count == 101);
assert(array_int_size(tmp) == 101); assert(array_int_size(tmp) == 101);
for(int i = 0; i < 100; i++) { 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(l, 100) == 17);
assert(*array_int_get(tmp, 100) == 17 * 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); array_int_reset(tmp);
#define FT(d,x) ((d) = (x) + 1) #define FT(d,x) ((d) = (x) + 1)