diff --git a/exercises/higher-oder-func/README.md b/exercises/higher-oder-func/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ea7405e6256f06f31791fdf3c5331f60ffe9a67b --- /dev/null +++ b/exercises/higher-oder-func/README.md @@ -0,0 +1,33 @@ +# Higher Order Functions + +Here is a small exercise that helps you understand function pointers and void pointers in C. + +## Instructions + +Implement some higher order functions for arrays. + +You will implement the following functions: + +* `map`: map a given array with a transform function to a new array. +* `filter`: filter a given array with a predicate function to a new array. +* `reduce`: "reduce" the array with a given initial accumulator and a reducer function to a final value. + +## Tips + +TBD + +## Testing + +The included makefile can be used to create and run the tests using the following commands: + +```bash +# run unit tests +make test + +# check memory leaks +make memcheck + +# clean all compilation files +make clean +``` + diff --git a/exercises/higher-oder-func/higher_order_func.c b/exercises/higher-oder-func/higher_order_func.c new file mode 100644 index 0000000000000000000000000000000000000000..973f971f658d57d5d44dfe53582e130fed044506 --- /dev/null +++ b/exercises/higher-oder-func/higher_order_func.c @@ -0,0 +1,17 @@ +#include "higher_order_func.h" +#include <stdlib.h> +#include <string.h> + +void *map(void *array, size_t n, size_t size, void (*fn)(void *elem)) { + // TODO: implement me! +} + +void *filter(void *array, size_t n, size_t size, size_t *new_size, + bool (*pred)(void *elem)) { + // TODO: implement me! +} + +void *reduce(void *array, size_t n, size_t size, void *initial_value, + void *(*reducer)(void *acc, void *elem)) { + // TODO: implement me! +} diff --git a/exercises/higher-oder-func/higher_order_func.h b/exercises/higher-oder-func/higher_order_func.h new file mode 100644 index 0000000000000000000000000000000000000000..c146a8f97f867cdce2e5e9d56ccb259b558c78e6 --- /dev/null +++ b/exercises/higher-oder-func/higher_order_func.h @@ -0,0 +1,12 @@ +#ifndef HIGHER_ORDER_FUNC_H +#define HIGHER_ORDER_FUNC_H + +#include <stddef.h> +#include <stdbool.h> + +void* map(void* array, size_t n, size_t size, void (*fn)(void* elem)); +void* filter(void* array, size_t n, size_t size, size_t* new_size, bool (*pred)(void* elem)); +void* reduce(void* array, size_t n, size_t size, void* initial_value, void* (*reducer)(void* acc, void * elem)); + +#endif + diff --git a/exercises/higher-oder-func/makefile b/exercises/higher-oder-func/makefile new file mode 100644 index 0000000000000000000000000000000000000000..765a4227c672256d2a7f1dead1ddd1ef637d394d --- /dev/null +++ b/exercises/higher-oder-func/makefile @@ -0,0 +1,36 @@ +### If you wish to use extra libraries (math.h for instance), +### add their flags here (-lm in our case) in the "LIBS" variable. +LIBS = -lm + +### +CFLAGS = -std=c99 +CFLAGS += -g +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -pedantic +CFLAGS += -Wmissing-declarations +CFLAGS += -DUNITY_SUPPORT_64 -DUNITY_OUTPUT_COLOR + +ASANFLAGS = -fsanitize=address +ASANFLAGS += -fno-common +ASANFLAGS += -fno-omit-frame-pointer + +.PHONY: test +test: tests.out + @./tests.out + +.PHONY: memcheck +memcheck: ./*.c ./*.h + @echo Compiling $@ + @$(CC) $(ASANFLAGS) $(CFLAGS) ../unity/unity.c ./*.c -o memcheck.out $(LIBS) + @./memcheck.out + @echo "Memory check passed" + +.PHONY: clean +clean: + rm -rf *.o *.out *.out.dSYM + +tests.out: ./*.c ./*.h + @echo Compiling $@ + @$(CC) $(CFLAGS) ../unity/unity.c ./*.c -o tests.out $(LIBS) + diff --git a/exercises/higher-oder-func/test_higher_order_func.c b/exercises/higher-oder-func/test_higher_order_func.c new file mode 100644 index 0000000000000000000000000000000000000000..175925df43fd7fb8c4b74154e2a58934e8cdf77c --- /dev/null +++ b/exercises/higher-oder-func/test_higher_order_func.c @@ -0,0 +1,222 @@ +#include "../unity/unity.h" +#include "higher_order_func.h" +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> + +#define INT_ARRAY_SIZE 100 + +static int int_arr[INT_ARRAY_SIZE]; +static int int_arr_copy[INT_ARRAY_SIZE]; + +void setUp(void) { + for (int i = 0; i < INT_ARRAY_SIZE; i++) { + int_arr[i] = i; + int_arr_copy[i] = i; + } +} + +void tearDown(void) {} + +static void id(void *element) { *(int *)element = *(int *)element; } + +static bool tautology(void *element) { + int *element_ptr = (int *)element; + return *element_ptr >= 0; +} + +static bool always_false(void *element) { return *(int *)element < 0; } + +static void double_int(void *element) { *((int *)element) *= 2; } + +static void plus_2(void *element) { *((int *)element) += 2; } + +static void *sum_int(void *acc, void *element) { + int *acc_ptr = (int *)acc; + int *element_ptr = (int *)element; + *acc_ptr += *element_ptr; + return acc_ptr; +} + +static void *product_int(void *acc, void *element) { + int *acc_ptr = (int *)acc; + if (*acc_ptr == 0) { + return acc_ptr; + } + int *element_ptr = (int *)element; + *acc_ptr *= *element_ptr; + return acc_ptr; +} + +static bool is_prime(void *element) { + int *element_ptr = (int *)element; + + if (*element_ptr < 2) { + return false; + } + for (int i = 2; i < *element_ptr; i++) { + if (*element_ptr % i == 0) { + return false; + } + } + return true; +} + +static bool is_even(void *element) { + int *element_ptr = (int *)element; + return *element_ptr % 2 == 0; +} + +static void test_map_empty_array(void) { + int *result = map(int_arr, 0, sizeof(int), NULL); + TEST_ASSERT_NULL(result); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); +} + +static void test_filter_empty_array(void) { + size_t new_size; + int *result = filter(int_arr, 0, sizeof(int), &new_size, NULL); + TEST_ASSERT_NULL(result); + TEST_ASSERT_EQUAL_INT(new_size, 0); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); +} + +static void test_reduce_empty_array(void) { + int inital_value = 0; + int *result = reduce(int_arr, 0, sizeof(int), &inital_value, NULL); + TEST_ASSERT_EQUAL_INT(*result, inital_value); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); +} + +static void test_map_id_array(void) { + int *result = map(int_arr, INT_ARRAY_SIZE, sizeof(int), id); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, result, INT_ARRAY_SIZE); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); + free(result); +} + +static void test_filter_totology_array(void) { + size_t new_size; + int *result = + filter(int_arr, INT_ARRAY_SIZE, sizeof(int), &new_size, tautology); + TEST_ASSERT_EQUAL_INT(new_size, INT_ARRAY_SIZE); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, result, INT_ARRAY_SIZE); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); + free(result); +} + +static void test_map_double_array(void) { + int *result = map(int_arr, INT_ARRAY_SIZE, sizeof(int), double_int); + for (int i = 0; i < INT_ARRAY_SIZE; i++) { + TEST_ASSERT_EQUAL_INT(int_arr[i] * 2, result[i]); + } + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); + free(result); +} + +static void test_map_plus_2_array(void) { + int *result = map(int_arr, INT_ARRAY_SIZE, sizeof(int), plus_2); + for (int i = 0; i < INT_ARRAY_SIZE; i++) { + TEST_ASSERT_EQUAL_INT(int_arr[i] + 2, result[i]); + } + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); + free(result); +} + +static void test_reduce_sum_array(void) { + int inital_value = 0; + int *result = + reduce(int_arr, INT_ARRAY_SIZE, sizeof(int), &inital_value, sum_int); + int expected = 0; + for (int i = 0; i < INT_ARRAY_SIZE; i++) { + expected += int_arr[i]; + } + TEST_ASSERT_EQUAL_INT(expected, *result); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); +} + +static void test_product_sum_array(void) { + int inital_value = 1; + int *result = + reduce(int_arr, INT_ARRAY_SIZE, sizeof(int), &inital_value, product_int); + int expected = 1; + for (int i = 1; i < INT_ARRAY_SIZE; i++) { + expected *= int_arr[i]; + } + TEST_ASSERT_EQUAL_INT(expected, *result); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); +} + +static void test_filter_is_prime_array(void) { + size_t new_size; + + int *result = + filter(int_arr, INT_ARRAY_SIZE, sizeof(int), &new_size, is_prime); + int expected[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, + 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}; + TEST_ASSERT_EQUAL_INT(new_size, sizeof(expected) / sizeof(int)); + TEST_ASSERT_EQUAL_INT_ARRAY(expected, result, sizeof(expected) / sizeof(int)); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); + free(result); +} + +static void test_filter_always_false_array(void) { + size_t new_size; + int *result = + filter(int_arr, INT_ARRAY_SIZE, sizeof(int), &new_size, always_false); + TEST_ASSERT_EQUAL_INT(new_size, 0); + TEST_ASSERT_NULL(result); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); +} + +static void test_filter_is_even_array(void) { + size_t new_size; + int *result = + filter(int_arr, INT_ARRAY_SIZE, sizeof(int), &new_size, is_even); + int expected[50]; + for (int i = 0; i < 50; i++) { + expected[i] = i * 2; + } + TEST_ASSERT_EQUAL_INT(new_size, 50); + TEST_ASSERT_EQUAL_INT_ARRAY(expected, result, sizeof(expected) / sizeof(int)); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); + free(result); +} + +static void test_is_even_and_sum(void) { + size_t new_size; + int *result = + filter(int_arr, INT_ARRAY_SIZE, sizeof(int), &new_size, is_even); + int inital_value = 0; + int *sum = reduce(result, new_size, sizeof(int), &inital_value, sum_int); + int expected = 0; + for (int i = 0; i < INT_ARRAY_SIZE; i++) { + if (int_arr[i] % 2 == 0) { + expected += int_arr[i]; + } + } + TEST_ASSERT_EQUAL_INT(new_size, 50); + TEST_ASSERT_EQUAL_INT(expected, *sum); + TEST_ASSERT_EQUAL_INT_ARRAY(int_arr, int_arr_copy, INT_ARRAY_SIZE); + free(result); +} + +int main(void) { + UnityBegin("test_higher_order_func.c"); + + RUN_TEST(test_map_empty_array); + RUN_TEST(test_filter_empty_array); + RUN_TEST(test_reduce_empty_array); + RUN_TEST(test_map_id_array); + RUN_TEST(test_filter_totology_array); + RUN_TEST(test_map_double_array); + RUN_TEST(test_reduce_sum_array); + RUN_TEST(test_filter_is_prime_array); + RUN_TEST(test_filter_always_false_array); + RUN_TEST(test_filter_is_even_array); + RUN_TEST(test_map_plus_2_array); + RUN_TEST(test_product_sum_array); + RUN_TEST(test_is_even_and_sum); + + return UnityEnd(); +}