Переваги чистої функції


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 правильне - наведене вище визначення чистого невірно. Чисто означає, що вихід залежить лише від входів функції; також, не повинно бути спостережуваних побічних ефектів. "Той самий результат для одного входу" є дуже неточним резюме цих вимог. 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

Коли ви говорите, що функція є "чистою", ви гарантуєте, що вона не має зовнішніх видимих ​​побічних ефектів (і, як зазначається в коментарі, якщо ви брешете, можуть статися погані речі). Знання того, що функція є "чистою", має переваги для компілятора, який може використовувати ці знання для певної оптимізації.

Ось що говорить документація GCC про pureатрибут:

чистий

Багато функцій не мають ефектів, крім поверненого значення, і їх повернене значення залежить лише від параметрів та / або глобальних змінних. Така функція може підлягати загальному усуненню підвиразу та оптимізації циклу, як і арифметичний оператор. Ці функції слід оголосити з атрибутом pure. Наприклад,

Відповідь Філіпа вже показує, як знання функції "чисто" може допомогти в оптимізації циклу.

Ось один для загального усунення підвиразу (наведено fooчисто):

Може стати:


3
Я не впевнений, чи робить це хтось, але чисті функції також дозволяють компілятору змінювати порядок, коли функцію викликають, якщо вона вважатиме, що переупорядкування буде корисним. Коли існує можливість побічних ефектів, компілятор повинен бути більш консервативним.
mpdonadio

@MPD - Так, це звучить розумно. Оскільки callінструкція є вузьким місцем для суперскалярних процесорів, допоможе компілятор.
ArjunShankar

Я неясно згадую використання компілятора DSP кілька років тому, який використовував би цей метод для отримання повернутих значень рано / пізно. Це дозволило мінімізувати зупинки трубопроводів.
mpdonadio

1
Чи можна попередньо розрахувати "foo (99)", оскільки 99 - це const, і foo завжди повертає однаковий результат? Може в якомусь двоступеневому компілюванні?
markwatson

1
@markwatson - я не впевнений. Можуть бути випадки, коли це просто неможливо. наприклад, якщо fooє частиною іншої одиниці компіляції (інший файл C), або в попередньо скомпільованій бібліотеці. В обох випадках компілятор не знає, що fooробить, і не може попередньо обчислити.
ArjunShankar

29

На додаток до можливих переваг під час виконання, чисту функцію набагато легше міркувати про читання коду. Крім того, набагато простіше перевірити чисту функцію, оскільки ви знаєте, що повертане значення залежить лише від значень параметрів.


3
+1, ваш пункт про тестування цікавий. Налаштування та знесення не потрібні.
ArjunShankar

15

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

це як розширення чистої функції

в якому ви маєте, крім явних аргументів функції x, y, решту Всесвіту (або все, з чим ваш комп'ютер може спілкуватися) як неявний потенційний вхід. Подібним чином, крім явного цілочисельного значення повернення, все, на що може писати ваш комп’ютер, неявно є частиною поверненого значення.

Повинно бути зрозуміло, чому набагато легше міркувати про чисту функцію, ніж про нечисту.


1
+1: Використання Всесвіту як потенційного вкладу - це дуже приємний спосіб пояснити різницю між чистим і нечистим.
ArjunShankar

справді, це ідея монад.
Kristopher Micinski

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.