Два условия, которые определяют функцию pure
, следующие:
- Без побочных эффектов (т.е. разрешены только изменения в локальной области)
- Всегда возвращать один и тот же вывод, учитывая тот же ввод
Если первое условие всегда верно, бывает ли когда-нибудь второе условие неверно?
Т.е. действительно ли это необходимо только с первым условием?
Ответы:
Вот несколько контрпримеров, которые не меняют внешний вид, но по-прежнему считаются нечистыми:
function a() { return Date.now(); }
function b() { return window.globalMutableVar; }
function c() { return document.getElementById("myInput").value; }
function d() { return Math.random(); }
(который, по общему признанию, меняет ГПСЧ, но не считается наблюдаемым)Доступа к непостоянным нелокальным переменным достаточно, чтобы нарушить второе условие.
Я всегда считаю два условия чистоты взаимодополняющими:
Термин « побочный эффект» относится только к первому - функции, изменяющей нелокальное состояние. Однако иногда операции чтения также рассматриваются как побочные эффекты: когда они являются операциями и включают также запись, даже если их основная цель - получить доступ к значению. Примерами для этого являются создание псевдослучайного числа, которое изменяет внутреннее состояние генератора, чтение из входного потока, который продвигает позицию считывания, или чтение с внешнего датчика, которое включает команду «выполнить измерение».
источник
prompt("you choose")
побочных эффектов нет, следует сделать шаг назад и уточнить значение побочных эффектов.«Нормальный» способ сформулировать, что такое чистая функция , - это ссылочная прозрачность . Функция чиста, если она прозрачна по ссылкам .
Ссылочная прозрачность , грубо говоря, означает, что вы можете заменить вызов функции ее возвращаемым значением или наоборот в любой точке программы, не изменяя смысла программы.
Так, например, если C
printf
были ссылочно прозрачными, эти две программы должны иметь одинаковое значение:printf("Hello");
и
5;
и все следующие программы должны иметь одно и то же значение:
5 + 5; printf("Hello") + 5; printf("Hello") + printf("Hello");
Потому как
printf
возвращает количество написанных символов, в данном случае 5.С
void
функциями это становится еще более очевидным . Если у меня есть функцияvoid foo
, тодолжно быть таким же, как
Т.е. поскольку
foo
ничего не возвращает, я должен иметь возможность заменить его ничем, не меняя смысла программы.Таким образом, ясно, что ни одно из них,
printf
ниfoo
является референциально прозрачным, и, следовательно, ни одно из них не является чистым. Фактически,void
функция никогда не может быть ссылочно прозрачной, если только она не является нерабочей.Я считаю, что это определение намного проще, чем то, которое вы дали. Он также позволяет вам применять его с любой степенью детализации: вы можете применять его к отдельным выражениям, функциям и целым программам. Это позволяет, например, говорить о такой функции:
func fib(n): return memo[n] if memo.has_key?(n) return 1 if n <= 1 return memo[n] = fib(n-1) + fib(n-2)
Мы можем проанализировать выражения, составляющие функцию, и легко прийти к выводу, что они не являются ссылочно прозрачными и, следовательно, не являются чистыми, поскольку они используют изменяемую структуру данных, а именно
memo
массив. Тем не менее, мы можем рассмотреть функцию и можно увидеть , что она является референциально прозрачной и таким чистой. Иногда это называют внешней чистотой. , т. Е. Функцией, которая кажется чистой внешнему миру, но реализуется нечистой внутри.Такие функции по-прежнему полезны, потому что, хотя примесь заражает все вокруг себя, внешний чистый интерфейс создает своего рода «барьер чистоты», где примесь заражает только три строки функции, но не просачивается в остальную часть программы. . Эти три строки намного легче проанализировать на правильность, чем всю программу.
источник
memo[n]
идемпотентна, а невозможность чтения из нее просто тратит циклы ЦП.memo[n] = ...
может сначала создать словарную статью, а затем сохранить в ней значение. Это оставляет окно, в течение которого другой поток может увидеть неинициализированную запись.Мне кажется, что второе описанное вами условие является более слабым ограничением, чем первое.
Позвольте мне привести пример. Предположим, у вас есть функция для добавления функции, которая также ведет журнал в консоли:
function addOneAndLog(x) { console.log(x); return x + 1; }
Второе условие, которое вы указали, выполнено: эта функция всегда возвращает один и тот же результат при одинаковом вводе. Однако это не чистая функция, поскольку она включает побочный эффект входа в консоль.
Чистая функция, строго говоря, это функция, которая удовлетворяет свойству ссылочной прозрачности . Это свойство, с помощью которого мы можем заменить приложение-функцию значением, которое оно производит, не меняя поведения программы.
Предположим, у нас есть функция, которая просто добавляет:
function addOne(x) { return x + 1; }
Мы можем заменить
addOne(5)
на в6
любом месте нашей программы, и ничего не изменится.Напротив, мы не можем заменить
addOneAndLog(x)
значение в6
любом месте нашей программы без изменения поведения, потому что первое выражение приводит к тому, что что-то записывается в консоль, а второе - нет.Мы рассматриваем любое из этих дополнительных действий, которые
addOneAndLog(x)
выполняются помимо возврата вывода, как побочный эффект .источник
Date.now()
он не является чистым / ссылочно прозрачным, но не потому, что он имеет побочные эффекты, а потому, что его результат зависит не только от его ввода.Может быть источник случайности извне системы. Предположим, что часть вашего расчета включает комнатную температуру. Тогда выполнение функции будет давать разные результаты каждый раз в зависимости от случайного внешнего элемента комнатной температуры. Состояние не изменяется при выполнении программы.
Во всяком случае, все, о чем я могу думать.
источник
Проблема с определениями FP в том, что они очень искусственные. Каждая оценка / вычисление имеет побочные эффекты для оценщика. Теоретически это правда. Отрицание этого показывает только то, что апологеты ФП игнорируют философию и логику: «оценка» означает изменение состояния некоторой интеллектуальной среды (машины, мозга и т. Д.). Таков характер процесса оценки. Без изменений - без «исчислений». Эффект может быть очень заметным: нагрев процессора или его выход из строя, выключение материнской платы в случае перегрева и так далее.
Когда вы говорите о ссылочной прозрачности, вы должны понимать, что информация о такой прозрачности доступна человеку как создателю всей системы и держателю семантической информации и может быть недоступна для компилятора. Например, функция может читать некоторый внешний ресурс, и в ее сигнатуре будет монада ввода-вывода, но она будет все время возвращать одно и то же значение (например, результат
current_year > 0
). Компилятор не знает, что функция всегда будет возвращать один и тот же результат, поэтому функция нечистая, но имеет ссылочно прозрачное свойство и может быть замененаTrue
константой.Итак, чтобы избежать такой неточности, мы должны различать математические функции и «функции» в языках программирования. Функции в Haskell всегда нечисты, и определение чистоты, связанной с ними, всегда очень условно: они работают на реальном оборудовании с реальными побочными эффектами и физическими свойствами, что неверно для математических функций. Это означает, что пример с функцией «printf» полностью неверен.
Но не все математические функции также чисты: каждая функция, которая имеет
t
(время) в качестве параметра, может быть нечистой:t
содержит все эффекты и стохастический характер функции: в общем случае у вас есть входной сигнал и вы не имеете представления о фактических значениях, он может даже шум.источник
да
Рассмотрим простой фрагмент кода ниже
public int Sum(int a, int b) { Random rnd = new Random(); return rnd.Next(1, 10); }
Этот код будет возвращать случайный вывод для того же заданного набора входов, однако он не имеет побочных эффектов.
Общий эффект обеих точек №1 и №2, которые вы упомянули при объединении вместе, означает: в любой момент времени, если функция
Sum
с одинаковым i / p заменяется ее результатом в программе, общее значение программы не изменяется . Это не что иное, как ссылочная прозрачность .источник
rnd
не экранирует функцию, поэтому тот факт, что ее состояние изменяется, не имеет значения для чистоты функции, но тот факт, чтоRandom
конструктор использует текущее время в качестве начального значения, означает, что есть «входы», отличные отa
иb
.