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

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

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

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

Также может быть вам интересно:

Как работать с JSON из Bash

Как получить поле JSON ответа из Bash скрипта или оболочки

Читать »

Об интересных задачах в программировании

Ох, сколько слов было сказано об интересных задачах. Но где же они?

Читать »
Фото Как сделать свою middleware в Django (с примерами)

Как сделать свою middleware в Django (с примерами)

Middleware или "промежуточное программное обеспечение" - элегантный способ установить общие правила обработки запросов и ответов приложения. Давайте напишем парочку middleware, чтобы понять, как они работают.

Фото Как настроить отправку почты из Django

Как настроить отправку почты из Django

Письма об ошибках, отчёты на почту, восстановление паролей - всё это полезно при работе с сайтом. Django предоставляет удобный способ это сделать с минимумом настроек!

Фото Добавляем поддержку медиа-файлов в Django проект

Добавляем поддержку медиа-файлов в Django проект

Современные сайты редко ограничиваются только текстом и вёрсткой. Часто в заметках красуются фотографии, а рядом с описанием товаров - их изображения.

Фото Настройка журналирования (логирования) в Python с примерами

Настройка журналирования (логирования) в Python с примерами

Во время работы программы часто нужно сохранять некоторые важные записи о процессе выполнения команды. В Python есть довольно мощный модуль для работы с логами - давайте разберёмся с тем, как его использовать.

Фото Нет слов, одни... однострочники

Нет слов, одни... однострочники

На днях вышел пост со списком полезных однострочников для JavaScript программистов. Памятуя Perl-овую молодость, заглянул туда.

Фото Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

В Django вы можете передавать данные в шаблоны посредством контекстов. Контекст передаётся из контроллера (view в терминах Django), однако, если одни и те же данные нужны в разных местах, лучше сделать свой контекст-процессор.

Фото Пример своей консольной команды в Django проекте

Пример своей консольной команды в Django проекте

Если вы работали с Django проектом, то, скорее всего, запускали команды из консоли (manage.py). В Django есть простой способ писать свои команды для управления проектом.

Фото Разграничение прав доступа на Django сайте

Разграничение прав доступа на Django сайте

Почти на любом веб-сайте необходимо разделять пользователей на группы и предоставлять им разные возможности. В Django есть довольно серьёзная система прав доступа для пользователей - давайте её рассмотрим!