Программы
Примеры решений ДЗ

Примеры решений ДЗ

Долгожданные примеры решений задач по Си

Примеры решений домашних заданий, часть 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 для установки.