Skip to content
Snippets Groups Projects
Commit 94f2c5ce authored by Mactavish's avatar Mactavish
Browse files

remove old contents

parent 708b94ac
Branches
No related tags found
No related merge requests found
# ALP4 Tutorial-2 # ALP4 Tutorial-3
This branch contains all materials for the second tutorial session. This branch contains all materials for the 3rd tutorial session.
## Agenda ## Agenda
- Recap TBD
- Discussion about programs and processes
- More on C Pointers
- Makefile
- C Programming exercises
- Q&A
# 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.
### Map
`map` takes a pointer to the target array, the length of the target array, the size of each element in the array and a pointer to a transformer function as parameters.
It should return a pointer to a newly allocated array, in which every element will be transformed using the transformer function.
For example:
```c
int double_it(void *elem) {
*((int *)elem) *= 2;
}
int arr[] = {1, 2, 3, 4, 5};
int *new_arr = map(arr, 5, sizeof int, double_it);
// the new_arr will be {2, 4, 6, 8, 9} and the old array arr is not affected
```
### Filter
`filter` takes a pointer to the target array, the length of the target array, the size of each element in the array, a pointer to a `size_t` variable that stores the size of the filtered array and a pointer to a predicate function as parameters.
It should return a pointer to a newly allocated array which contains only those elements that fulfill the requirements posed by the predicate function.
For example:
```c
bool is_even(void *elem) {
return *((int *)elem) % 2 == 0;
}
int arr[] = {1, 2, 3, 4, 5};
size_t new_size = 0;
int *new_arr = filter(arr, 5, sizeof int, &new_size, is_even);
// the new_arr will be {2, 4} and the old array arr is not affected, new_size will be 2
```
The parameter `new_size` is necessary, otherwise there is no way of knowing the size of the filtered array since we can only deduct the size of an array only from a pointer.
### Reduce
`reduce` takes a pointer to the target array, the length of the target array, the size of each element in the array, a pointer to the initial accumulator variable and a pointer to a reducer function as parameters.
It should return a pointer to that initial accumulator.
The purpose of `reduce` is to apply a given function to a sequence of elements from left to right and reduces it to a single value using a so called accumulator.
For example:
```c
void* sum_int(void *acc, void *elem) {
int *acc_ptr = (int *)acc;
int *elem_ptr = (int *)elem;
*acc_ptr += *elem_ptr;
return acc_ptr;
}
int arr[] = {1, 2, 3, 4, 5};
int acc = 0;
int *result = reduce(arr, 5, sizeof int, &acc, sum_int);
// acc will be 1+2+3+4+5 = 15 and result actually points to acc
```
## Tips
We want the `map`, `filter` and `reduce` functions to be as generic as they can, so as you can see, the return values and the parameters of the given functions are all using `void *` pointers.
For `map` and `filter`, you will have to use `malloc` or `calloc` to allocate a new array. If the given `length` is 0, then just return a `NULL`.
In order to use the transformer/predicate/reducer function, you will have to calculate the offset of the element with the give `size`. Because you don't have information of the `type` of the array elements, so you have to calculate the offset of the elements manually.
Because we only have `void *` pointer to the elements, we can cast it into `char *` and then calculate the offset per bytes.
## 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
```
#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)) {
if (n == 0) {
return NULL;
}
void *result = malloc(n * size);
// copy original array to result byte by byte
memcpy((char *)result, (char *)array, n * size);
for (size_t i = 0; i < n; i++) {
// cast it to char* to make it byte addressable
fn((char *)result + i * size);
}
return result;
}
void *filter(void *array, size_t n, size_t size, size_t *new_size,
bool (*pred)(void *elem)) {
*new_size = 0;
size_t count = 0;
if (n == 0) {
return NULL;
}
for (size_t i = 0; i < n; i++) {
void *element = (char *)array + i * size;
if (pred(element)) {
count++;
}
}
if (count == 0) {
return NULL;
}
void *result = malloc(count * size);
count = 0;
for (size_t i = 0; i < n; i++) {
void *element = (char *)array + i * size;
if (pred(element)) {
void *result_element = (char *)result + count * size;
memcpy(result_element, element, size);
count++;
}
}
*new_size = count;
return result;
}
void *reduce(void *array, size_t n, size_t size, void *initial_value,
void *(*reducer)(void *acc, void *elem)) {
if (n == 0) {
return initial_value;
}
void *accumulator = initial_value;
for (size_t i = 0; i < n; i++) {
void *element = (char *)array + i * size;
accumulator = reducer(accumulator, element);
}
return accumulator;
}
#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
### 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)
#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();
}
# Define required macros here
PROG = mysystem
OBJS = mysystem.o
CFLAG = -Wall -g
CC = gcc
INCLUDE = -I.
# binary target
$(PROG): $(OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $(OBJS)
# implicit rule for building .o from .c
%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS) $(INCLUDES)
.PHONY: clean
# clean all the .o and executable files
clean:
rm -rf *.o $(PROG)
/**
* File: mysystem.c
* ----------------
* A program containing an implementation of a working shell that can
* execute entered text commands. It relies on our own implementation
* of system(), called mysystem(), that creates a process to execute
* a given shell command.
*/
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
// The max-length shell command the user can enter
static const size_t COMMAND_MAX_LEN = 2048;
/* Function: mysystem
* ------------------
* Parameters:
* command - the shell command to execute in another process
* Returns: the exit status of the command execution
*
* This function executes the given shell command using /bin/sh.
* It executes it in another process, and waits for that process
* to complete before returning. This function returns its exit status.
*
* Hint: use fork(), exec() family of functions, and waitpid()
* use "/bin/sh" and "-c" als the first two arguments to exec() family of
* Further information: https://www.baeldung.com/linux/exec-functions
*/
// Uncomment when implemented
static int mysystem(char *command) {
pid_t pidOrZero = fork();
if (pidOrZero == 0) {
char *arguments[] = {"/bin/sh", "-c", command, NULL};
execvp(arguments[0], arguments);
// If the child gets here, there was an error
exit(errno);
}
// If we are the parent, wait for the child
int status;
waitpid(pidOrZero, &status, 0);
return WIFEXITED(status) ? WEXITSTATUS(status) : -WTERMSIG(status);
}
int main(int argc, char *argv[]) {
char command[COMMAND_MAX_LEN];
while (true) {
printf("> ");
fgets(command, sizeof(command), stdin);
// If the user entered Ctl-d, stop
if (feof(stdin)) {
break;
}
// Remove the \n that fgets puts at the end
command[strlen(command) - 1] = '\0';
// TODO: use mysystem instead of system
int commandReturnCode = mysystem(command);
printf("return code = %d\n", commandReturnCode);
}
printf("\n");
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment