Долгожданные примеры решений задач по Си
Примеры решений домашних заданий, часть 2.
Написать функции filter, reduce - аналоги функций python.
#include <stdio.h> #include <stdlib.h> #include <string.h> /** * Функция фильтрации * * Отфильтровывает из входящего массива элементы, * проходящие по критериям фильтрующей функции. * Результат складывает в результирующий массив. * * Возвращает количество елементов, прошедших фильтрацию. * Вопрос: какой тип стоит здесь использовать вместо int? */ int filter( void *from, void *to, size_t count, size_t size, int (*where)(void *) ) { // Для использования арифметики указателей, // преобразуем в указатели на байты. char *from_bytes = (char *)from; char *to_bytes = (char *)to; int index = 0; for (size_t i = 0; i < count; i++) { if (where(from_bytes + size * i)) { // Копируем данные из исходного в целевой массив. // Вопрос: как это ещё можно было сделать? memcpy(to_bytes + size * index, from_bytes + size * i, size); index++; } } return index; } /** * Функция свёртки (левой) * * Первый шаг - положить в результат * первый элемент входного массива. * Далее - идём слева направо и применяем операцию * к результату и элементу массива и складываем в результат. * * Вопрос: что делать в случае пустого входного массива? */ void reduce( void *array, void *result, size_t count, size_t size, void (*consumer)(void *, void *) ) { if (size == 0) { return; } char *array_bytes = (char *)array; char *result_bytes = (char *)result; memcpy(result_bytes, array_bytes, size); for (int i = 1; i < count; i++) { consumer(result_bytes, array_bytes + size * i); } } /** * Проверка на чётность int-а */ int is_odd(int* a) { return (*a) % 2 == 0; } /** * Сложение int-ов */ void sum(int *n_1, int *n_2) { *n_1 = *n_1 + *n_2; } /** * Демонстрация работы */ int main(void) { int a[6] = {1, 1, 2, 3, 5, 8}; int b[6]; int size = filter(a, b, sizeof(a) / sizeof(int), sizeof(int), is_odd); printf("Фильтр: "); for (int i = 0; i < size; ++i) { printf("%d ", b[i]); } printf("\n"); int c; reduce(a, &c, sizeof(a) / sizeof(int), sizeof(int), sum); printf("Свёртка: %d\n", c); return 0; }
Написать на C API функцию Python3, получающую по объекту его счётчик ссылок.
Зная, что у меня замечательные студенты, которые умеют гуглить, решил дать им это задание. Пусть и тема о заголовочных файлах впереди – так получат хоть "шапочное" знакомство. А написать свой модуль на Си – знаю, руки чешутся!
src/counter.c
:
/** * Работаем с Python - его и подключаем. * Понадобится пакет python3-dev (может отличаться) */ #include <Python.h> /** * Требуемая функция - получает 2 Python-объекта: * self и аргументы функции. Нас интересует только 2-ой. * * Вернёт также Python-объект (у нас же всё "объект"). * Само собой, объектов не существует, это Сишные структуры. * * Оставляю за вами возможность рассмотреть структуру PyObject * - она не сложная, лежит в Python.h */ static PyObject* ref_count(PyObject* self, PyObject* args) { PyObject* object; /** * Документацию по PyArg_ParseTuple можно посмотреть здесь * https://docs.python.org/3/c-api/arg.html * * args - структура, которую мы парсим * "O" - парсим в объект * Указатель на object - куда мы это положим * * Если мы получили то, что не является объектом – NULL */ if (! PyArg_ParseTuple(args, "O", &object)) { return NULL; } /** * По ссылке выше также можно отыскать описание Py_BuildValue. * Собираем из байтов указатель на PyObject. * * "i" - это наш int. Именно его мы получим благодаря функции * Py_REFCNT, который получает из PyObject счётчик ссылок. * * Доп. задание - не использовать Py_REFCNT для этой функции. */ return Py_BuildValue("i", Py_REFCNT(object)); } // Python-овский docstring - документация функции static char ref_docs[] = "count(): get reference count"; // Описание метода. Да, тоже структура. static PyMethodDef ref_funcs[] = { { "count", // Название "метода" (PyCFunction)ref_count, // Сама функция METH_VARARGS, // Тип параметров ref_docs // Документация функции }, // "Нуль-терминатор" - функций может быть много, // надо знать, когда останоиться. {NULL, NULL, 0, NULL} }; // Описание модуля static struct PyModuleDef ref_def = { PyModuleDef_HEAD_INIT, // Стандартная инициализация модуля "ref", // Название модуля NULL, // Документация - обойдёмся без неё -1, // Размер – нам не нужно ref_funcs, // Методы модуля }; // Сама функция инициализации модуля PyMODINIT_FUNC PyInit_ref(void) { return PyModule_Create(&ref_def); }
Всё это можно собрать в Python модуль с помощью следующего setup.py
:
import os from setuptools import setup, Extension setup( name='ref', version='0.0.1', zip_safe=False, include_package_data=True, ext_modules=[ Extension( 'ref', ['src/counter.c'], ) ], )
Ну и python3 setup.py install
для установки.