Преимущества чистой функции


82

Сегодня читал про чистую функцию, запутался в ее использовании:

Функция называется чистой, если она возвращает тот же набор значений для одного и того же набора входных данных и не имеет никаких наблюдаемых побочных эффектов.

например strlen(), это чистая функция, а rand()нечистая.

http://ideone.com/33XJU

Вышеупомянутая программа ведет себя так же, как и при отсутствии pureобъявления.

Каковы преимущества объявления функции как pure[если нет изменений в выходе]?


7
Да - посмотрите сгенерированную сборку.
— Филип Кендалл

4
Я не думаю, что это определение чистоты является правильным - printfнапример, оно подходит (вызов его дважды с одними и теми же аргументами дает одно и то же возвращаемое значение), но оно не является чистым.
— tdammers

14
@tdammers: Действительно, ему не хватает роли ...and no side-effects....
— Frerich Raabe

2
@Ben: откуда берется энтропия? Здесь мы имеем дело с (теоретически) детерминированными машинами, единственный способ получить в них истинную энтропию - из внешних источников, что означает побочные эффекты. Конечно, мы могли бы позволить языкам программирования определять недетерминированные функции, делая вид, что технических побочных эффектов нет, а функции действительно недетерминированы; но если мы это сделаем, большая часть практических преимуществ отслеживания чистоты будет потеряна.
— tdammers

3
tdammers правильный - определение pure, данное выше, неверно. Чистый означает, что вывод зависит только от входов функции; также не должно быть заметных побочных эффектов. «Одинаковый результат для одного и того же входа» - это очень неточное описание этих требований. en.wikipedia.org/wiki/Pure_function
— Dancrumb

Ответы:


145

pure позволяет компилятору знать, что он может сделать определенную оптимизацию функции: представьте себе небольшой код вроде

С чистой функцией компилятор может знать, что ему нужно выполнить оценку fun(10)только один раз, а не 1000 раз. Для сложной функции это большой выигрыш.


— То

@mob Что ты имеешь в виду? Почему бы и нет?
— Конрад Рудольф

15
Потому что вы можете изменить строку (последовательность символов, начинающуюся с некоторого адреса), не изменяя ввод (указатель на адрес, с которого начинается строка), то есть вы не можете запоминать ее. Это была бы только чистая функция на языке с неизменяемыми строками (скажем, Java).
— моб

5
@KonradRudolph: Представьте себе строку длиной 1000. Позвони strlenему. Тогда снова. То же самое, да? Теперь измените второй символ как \0. Все strlenеще возвращает 1000 сейчас? Начальный адрес такой же (== ввод такой же), но функция теперь возвращает другое значение.
— Майк Бейли,

5
@mob Хорошее возражение, очевидно, вы правы. Меня ввел в заблуждение тот факт, что даже книги утверждают, что strlen(в GCC / glibc) на самом деле чистый. Но взгляд на реализацию glibc показал, что это неверно.
— Конрад Рудольф

34

Когда вы говорите, что функция «чистая», вы гарантируете, что она не имеет видимых извне побочных эффектов (и, как говорится в комментарии, если вы солгаете, могут произойти неприятности). Знание того, что функция «чистая» имеет преимущества для компилятора, который может использовать это знание для выполнения определенных оптимизаций.

Вот что говорится об атрибуте в документации GCCpure :

чистый

Многие функции не имеют никаких эффектов, кроме возвращаемого значения, а их возвращаемое значение зависит только от параметров и / или глобальных переменных. Такая функция может подвергаться обычному исключению подвыражения и оптимизации цикла, как и арифметический оператор. Эти функции следует объявлять с атрибутом pure. Например,

Ответ Филиппа уже показывает, как знание того, что функция «чиста», может помочь в оптимизации цикла.

Вот одно для общего исключения подвыражения (дано fooчисто):

Может стать:


3
Я не уверен, что это делают, но чистые функции также позволяют компилятору переупорядочивать при вызове функции, если он сочтет это полезным. Когда есть вероятность побочных эффектов, компилятор должен быть более консервативным.
— mpdonadio

@MPD - Да, звучит разумно. А поскольку callинструкция является узким местом для суперскалярных процессоров, некоторая помощь компилятора может помочь.
— ArjunShankar

Я смутно вспоминаю, как несколько лет назад использовал компилятор DSP, который использовал бы эту технику для получения возвращаемых значений раньше / позже. Это позволило свести к минимуму срыв трубопровода.
— mpdonadio

1
Можно ли предварительно вычислить «foo (99)», поскольку 99 является константой, а foo всегда будет возвращать один и тот же результат? Может, в какой-нибудь двухэтапной компиляции?
— markwatson

1
@markwatson - я не уверен. Бывают случаи, когда это просто невозможно. например, if fooявляется частью другого модуля компиляции (другого файла C) или находится в предварительно скомпилированной библиотеке. В обоих случаях компилятор не знает, что fooделает, и не может предварительно вычислить.
— ArjunShankar

29

Помимо возможных преимуществ во время выполнения, при чтении кода гораздо проще рассуждать о чистой функции. Кроме того, гораздо проще протестировать чистую функцию, поскольку вы знаете, что возвращаемое значение зависит только от значений параметров.


3
+1, ваш тезис о тестировании интересен. Не требуется установка и демонтаж.
— ArjunShankar

15

Нечистая функция

похожа на продолжение чистой функции

в котором у вас есть, помимо явных аргументов функции x, y, остальная часть вселенной (или все, с чем ваш компьютер может взаимодействовать) в качестве неявного потенциального ввода. Точно так же, помимо явного целочисленного возвращаемого значения, все, что ваш компьютер может писать, неявно является частью возвращаемого значения.

Должно быть ясно, почему проще рассуждать о чистой функции, чем о нечистой.


1
+1: Использование вселенной в качестве потенциального входа - очень хороший способ объяснить разницу между чистым и нечистым.
— ArjunShankar

действительно, это идея монад.
— Кристофер Мицински

7

В качестве дополнения я хотел бы упомянуть, что C ++ 11 несколько кодирует вещи с помощью ключевого слова constexpr. Пример:

Ограничения на использование constexpr делают эту функцию доказуемо чистой. Таким образом, компилятор может более агрессивно оптимизировать (просто убедитесь, что вы используете хвостовую рекурсию, пожалуйста!) И оценивать функцию во время компиляции, а не во время выполнения.

Итак, чтобы ответить на ваш вопрос, заключается в том, что если вы используете C ++ (я знаю, что вы сказали C, но они связаны), написание чистой функции в правильном стиле позволяет компилятору делать с функцией всевозможные классные вещи: -)


4

В общем, чистые функции имеют 3 преимущества перед нечистыми функциями, которыми может воспользоваться компилятор:

Кеширование

Допустим, у вас есть чистая функция f которая вызывается 100000 раз, поскольку она детерминирована и зависит только от ее параметров, компилятор может вычислить ее значение один раз и использовать при необходимости.

Параллелизм

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

Передача по ссылке

Функция f(struct t)получает свой аргумент tпо значению, и, с другой стороны, компилятор может передать tпо ссылке, fесли она объявлена ​​как чистая, при этом гарантируя, что значение tне изменится и даст прирост производительности


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

Не нужно создавать объекты или имитировать подключения к БД / файловой системе.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.