Программы
Колбеки и функции высших порядков

Колбеки и функции высших порядков

Немного функционального программирования в Си

Да, я знаю, что "не приятно" в Си делать функции высших порядков (когда функции используют другие функции в качестве аргументов)... И на самом деле это частое заблуждение! Частый случай использования функций – навешивание колбеков – вызовов, которые должны случиться, когда произойдёт другое действие. Например, приход нового сетевого пакета. Да множество функций API сетевогои системного программирования просто эксплуатирует колбеки и тут и там! А это как раз частный случай функций высшего порядка...

Опять же, в Си нет разграничения данных и кода – код также данные, на которые мы также можем получить указатель и передать переменной.

Функции как аргументы функций

Пожалуй, самый простой пример, когда полезно использовать функцию как агрумент другой функции – сортировка. Например, когда мы в Python сортируем итерабельные объекты:

>>> lst = [122, 43, 1, 452, 87, 5]
>>> sorted(lst, key=lambda x: len(str(x)))
[1, 5, 43, 87, 122, 452]

– мы передаём параметром функцию, которая "оценивает" каждый элемент на "больше-меньше". Другой подход (более каноничный) мы можем увидеть в языке Perl:

my @lst = (122, 43, 1, 452, 87, 5);
@result = sort { length($a) <=> length($b) } @lst;
print join(" ", @result);
^D
1 5 43 87 122 452

– здесь мы передаём функции sort функцию сравнения от двух сравниваемых значений $a и $b, которая вернёт -1 - если $b > $a, 0 - если $b == $a, 1 - если $a > $b.

На Си это будет выглядеть так:

#include <stdio.h>
#include <stdlib.h>  // Подключаем для qsort
#include <string.h>

int cmpfunc (const void *a, const void *b) {
    char a_str[10];
    char b_str[10];

    sprintf(a_str, "%d", *(int *)a);
    sprintf(b_str, "%d", *(int *)b);

    return strlen(a_str) - strlen(b_str);
}

int main () {
    int values[] = { 122, 43, 1, 452, 87, 5 };

    qsort(values, 6, sizeof(int), cmpfunc);

    for(int n = 0; n < 6; n++) {   
        printf("%d ", values[n]);
    }

    return(0);
}

Прототип функции qsort (man qsort):

void qsort(         // Функция ничего не вернёт
    void *base,     // Указатель на что-либо
    size_t nitems,  // Беззнаковое целое для "размеров"
    size_t size,
    int (*compar)(const void *, const void *)
);

Параметры:

  • base − указатель на первый элемент массива, который мы сортируем.
  • nitems − количество элементов массива.
  • size − размер в байтах каждого элемента массива.
  • compar − функция сравнения 2-х элементов.

Напишем свою функцию высшего порядка

map – применить функцию к каждому элементу "коллекции".

#include <stdio.h>
#include <stdlib.h>

void map(
  void *from,
  void *to,
  size_t count,
  size_t size,
  void (*modify)(void *, void *)
) {
  /**
   * Адресной арифметики на void-указателях нет.
   * Поэтому преобразуем в байт-указатели.
   */
  char *from_bytes = (char *)from;
  char *to_bytes = (char *)to;

  for (int i = 0; i < count; ++i) {
    modify(from_bytes + i * size, to_bytes + i * size);
  }
}

/**
 * Можем описать любую функцию, реализующую прототип,
 * которая всё также будет изменять каждый элемент
 * в исходном массиве и кладёт в результирующий.
 */
void modify_int(void *from, void *to) {
  int *a = (int *)from;
  int *b = (int *)to;
  *b = (*a) * (*a);
}

int main() {
  int a[] = {1, 2, 3, 4, 5};
  int b[5];

  map(a, b, 5, sizeof(int), modify_int);

  for (int i = 0; i < 5; ++i) {
    printf("%d\n", b[i]);
  }

  return 0;
}

Callback

Часто бывает нужно выполнить какое-то действие после другого (что очевидно :)). Что не очевидно – не всегда это можно сделать последовательными вызовами 2-х функций. Подробнее это мы рассмотрим на практике в следующих темах – "обратные вызовы" – частый инструмент API в Си библиотеках.

Чисто технически это не сильно отличается от функций высшего порядка.

ДЗ

Написать функции filter, reduce - аналоги функций python.

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

Ключевое слово Void в Си

Значений у Void в Си несколько. Давайте рассмотрим их

Читать »

Структуры и объединения

Базовые структуры в Си. Общие принципы работы с ними

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

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

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

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

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

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

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

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

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

Фото Шаблон разработки ПО — Model View Controller (MVC)

Шаблон разработки ПО — Model View Controller (MVC)

MVC - один из самых распространённых архитектурных шаблонов разработки. Часто используется в различных фреймворках. В том числе и в Django.

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

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

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

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

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

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

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

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

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

Фото Пользователи и их создание в Django - своя регистрация на сайте

Пользователи и их создание в Django - своя регистрация на сайте

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