Чистые функции: подразумевается ли «без побочных эффектов» «всегда один и тот же вывод при одинаковом вводе»? - PullRequest
0 голосов
/ 05 марта 2019

Два условия, которые определяют функцию как pure, следующие:

  1. Никаких побочных эффектов (т. Е. Допускаются только изменения в локальной области действия)
  2. Всегда возвращать одно и то жевыходной сигнал при одинаковом входном сигнале

Если первое условие всегда истинно, есть ли случаи, когда второе условие неверно?

Т.е. действительно ли это необходимо только при первом условии

Ответы [ 6 ]

0 голосов
/ 08 марта 2019

Проблема с определениями FP в том, что они очень искусственные.Каждая оценка / расчет имеет побочные эффекты для оценщика.Это теоретически верно.Отрицание этого показывает только то, что апологеты ФП игнорируют философию и логику: «оценка» означает изменение состояния некоторой интеллектуальной среды (машины, мозга и т. Д.).Это характер процесса оценки.Без изменений - без «исчислений».Эффект может быть очень заметен: нагрев процессора или его сбой, выключение материнской платы в случае перегрева и т. Д.

Когда вы говорите о ссылочной прозрачности, вы должны понимать, что информация о такой прозрачности доступнадля человека как создателя всей системы и держателя семантической информации и может быть недоступен для компилятора.Например, функция может читать некоторый внешний ресурс, и в ее сигнатуре будет монада ввода-вывода, но она будет постоянно возвращать одно и то же значение (например, результат current_year > 0).Компилятор не знает, что функция всегда будет возвращать один и тот же результат, поэтому функция нечиста, но имеет свойство прозрачности по ссылкам и может быть заменена константой True.

Поэтому, чтобы избежать такой неточности, следует различатьматематические функции и «функции» в языках программирования.Функции в Haskell всегда нечисты, и определение чистоты, связанной с ними, всегда очень условно: они работают на реальном оборудовании с реальными побочными эффектами и физическими свойствами, что неправильно для математических функций.Это означает, что пример с функцией «printf» абсолютно некорректен.

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

0 голосов
/ 05 марта 2019

Если первое условие всегда истинно, есть ли случаи, когда второе условие неверно?

Да

Рассмотрим простой кодфрагмент ниже

public int Sum(int a, int b) {
    Random rnd = new Random();
    return rnd.Next(1, 10);
}

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

Общий эффект обеих точек # 1 и #2, которое вы упомянули, когда объединено вместе, означает: В любой момент времени, если функция Sum с тем же i / p заменяется результатом в программе, общее значение программы не изменяется .Это не что иное, как Ссылочная прозрачность .

0 голосов
/ 05 марта 2019

"Нормальный" способ выразить, что такое чистая функция , в терминах ссылочная прозрачность .Функция pure , если она ссылочно-прозрачная .

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

Так, например, если C printf были ссылочно прозрачными, эти две программы должны иметь одинаковое значение:

printf("Hello");

и

5;

и все следующие программы должны иметь одинаковое значение:

5 + 5;

printf("Hello") + 5;

printf("Hello") + printf("Hello");

Поскольку printf возвращает количество написанных символов, в данном случае 5.

Это становится еще более очевидным с void функциями.Если у меня есть функция void foo, то

foo(bar, baz, quux);

должно быть таким же, как

;

Т.е. поскольку 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.Однако мы также можем взглянуть на функцию и увидеть, что она является ссылочно прозрачной и, следовательно, чистой.Это иногда называют внешней чистотой , то есть функцией, которая кажется чистой для внешнего мира, но реализованной нечистым внутри.

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

0 голосов
/ 05 марта 2019

Мне кажется, что второе условие, которое вы описали, является более слабым ограничением, чем первое.

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

function addOneAndLog(x) {
  console.log(x);
  return x + 1;
}

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

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

Предположим, у нас есть функция, которая просто добавляет:

function addOne(x) {
  return x + 1;
}

Мы можемзамените addOne(5) на 6 где-нибудь в нашей программе, и ничего не изменится.

В отличие от этого, мы не можем заменить addOneAndLog(x) значением 6 где-либо в нашей программе без изменения поведения, потому что первое выражение приводит кв то, что записывается на консоль, тогда как второй нет.

Мы рассматриваем любое из этих дополнительных действий, которые addOneAndLog(x) выполняет, кроме возврата результата, как побочный эффект .

0 голосов
/ 05 марта 2019

Там может быть источник случайности извне системы.Предположим, что часть вашего расчета включает комнатную температуру.Затем выполнение функции будет каждый раз давать разные результаты в зависимости от случайного внешнего элемента комнатной температуры.Состояние не изменяется при выполнении программы.

Все, что я могу придумать, так или иначе.

0 голосов
/ 05 марта 2019

Вот несколько контрпримеров, которые не изменяют внешний объем, но все еще считаются нечистыми:

  • function a() { return Date.now(); }
  • function b() { return window.globalMutableVar; }
  • function c() { return document.getElementById("myInput").value; }
  • function d() { return Math.random(); } (который по общему признанию изменяет PRNG, но не считается наблюдаемым)

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

Я всегда считаю два условия чистоты взаимодополняющими:

  • оценка результата не должна оказывать влияние на состояние стороны
  • результат оценки не должен быть затронут побочным состоянием

Термин побочный эффект относится только к первому, функция, изменяющая нелокальное состояние,Однако иногда операции чтения также рассматриваются как побочные эффекты: когда они операции и также включают запись, даже если их основная цель - получить доступ к значению.Примерами этого являются генерация псевдослучайного числа, которое изменяет внутреннее состояние генератора, считывание из входного потока, который продвигает позицию считывания, или считывание с внешнего датчика, который включает команду «выполнить измерение».

...