Я видел это в императивных парадигмах
F (X) + F (х)
может не совпадать с:
2 * Р (х)
Но в функциональной парадигме все должно быть так же. Я попытался реализовать оба случая в Python и Scheme , но для меня они выглядят довольно просто одинаково.
Какой пример мог бы указать на разницу с данной функцией?
f(x++)+f(x++)
может не совпадать с2*f(x++)
(в С это особенно замечательно, когда подобные вещи прячутся в макросах - разве я сломал себе нос по этому поводу? Бьюсь об заклад)f(x++)+f(x++)
может быть абсолютно любым, поскольку вызывает неопределенное поведение. Но на самом деле это не относится к ссылочной прозрачности - что не помогло бы для этого вызова, оно «неопределено» для ссылочно-прозрачных функций, как и вsin(x++)+sin(x++)
. Может быть 42, мог отформатировать ваш жесткий диск, мог бы иметь демонов, летящих из носа пользователей ...Ответы:
Ссылочная прозрачность, относящаяся к функции, означает, что вы можете определить результат применения этой функции, только взглянув на значения ее аргументов. Вы можете написать ссылочно прозрачные функции на любом языке программирования, например, Python, Scheme, Pascal, C.
С другой стороны, в большинстве языков вы также можете писать нереферентно прозрачные функции. Например, эта функция Python:
не является ссылочно прозрачным, фактически вызывает
а также
будет выдавать разные значения для любого аргумента
x
. Причина этого в том, что функция использует и изменяет глобальную переменную, поэтому результат каждого вызова зависит от этого изменяющегося состояния, а не только от аргумента функции.Haskell, чисто функциональный язык, строго отделяет оценку выражений, в которой применяются чистые функции и которая всегда прозрачна по ссылкам, от выполнения действия (обработки специальных значений), которое не является ссылочно прозрачным, то есть выполнение того же действия может иметь каждый раз, когда другой результат.
Итак, для любой функции Haskell
и любое целое число
x
, это всегда верно, чтоПримером действия является результат библиотечной функции
getLine
:В результате вычисления выражения эта функция (фактически константа) в первую очередь выдает чистое значение типа
IO String
. Значения этого типа являются значениями, как и любые другие: вы можете передавать их, помещать в структуры данных, составлять их с помощью специальных функций и так далее. Например, вы можете составить список действий следующим образом:Действия особенные в том, что вы можете указать среде выполнения Haskell выполнить их, написав:
В этом случае, когда ваша программа на Haskell запущена, среда выполнения проходит через действие, связанное с ним,
main
и выполняет его, возможно, вызывая побочные эффекты. Следовательно, выполнение действия не является прозрачным по ссылкам, поскольку выполнение одного и того же действия два раза может привести к различным результатам в зависимости от того, что среда выполнения получает в качестве входных данных.Благодаря системе типов Haskell действие никогда не может использоваться в контексте, где ожидается другой тип, и наоборот. Итак, если вы хотите найти длину строки, вы можете использовать
length
функцию:вернет 5. Но если вы хотите найти длину строки, считываемой из терминала, вы не можете написать
потому что вы получаете ошибку типа:
length
ожидается ввод списка типов (а String - это действительно список), ноgetLine
это значение типаIO String
(действие). Таким образом, система типов гарантирует, что значение действия наподобиеgetLine
(чье выполнение выполняется за пределами основного языка и которое может быть нереферентно прозрачным) не может быть скрыто внутри значения типа без действияInt
.РЕДАКТИРОВАТЬ
Чтобы ответить на вопрос exizt, вот небольшая программа на Haskell, которая читает строку из консоли и печатает ее длину.
Основное действие состоит из двух подзадач, которые выполняются последовательно:
getline
типаIO String
,putStrLn
типаString -> IO ()
по ее аргументу.Точнее, второе действие построено
line
к значению, прочитанному первым действием,length
(вычислить длину как целое число) и затемshow
(превратить целое число в строку),putStrLn
к результатуshow
.На этом этапе второе действие может быть выполнено. Если вы ввели «Hello», он напечатает «5».
Обратите внимание, что если вы получаете значение из действия, используя
<-
нотацию, вы можете использовать это значение только внутри другого действия, например, вы не можете написать:потому что
show (length line)
имеет тип,String
тогда как запись do требует, чтобы за действием (getLine
типаIO String
) следовало другое действие (например,putStrLn (show (length line))
типаIO ()
).РЕДАКТИРОВАТЬ 2
Определение реляционной прозрачности Йоргом В. Миттагом является более общим, чем мое (я одобрил его ответ). Я использовал ограниченное определение, потому что пример в вопросе фокусируется на возвращаемом значении функций, и я хотел проиллюстрировать этот аспект. Однако RT в целом относится к смыслу всей программы, включая изменения глобального состояния и взаимодействия с окружающей средой (IO), вызванные оценкой выражения. Итак, для правильного общего определения вы должны обратиться к этому ответу.
источник
IO
довольно легко реализовать тип языка Haskell на любом языке с лямбдами и обобщениями, но поскольку каждый может позвонитьprintln
напрямую, реализацияIO
не гарантирует чистоту; это будет просто соглашение.getLine
не относитесь к прозрачности, неверна. Вы представляете,getLine
как будто он оценивает или уменьшает некоторую строку, конкретная строка которой зависит от ввода пользователя. Это неверноIO String
не содержит String больше, чемMaybe String
делает.IO String
это рецепт для, возможно, получения String и, как выражение, он такой же чистый, как и любой другой в Haskell.Однако это не то, что означает ссылочная прозрачность. RT означает, что вы можете заменить любое выражение в программе результатом вычисления этого выражения (или наоборот) без изменения значения программы.
Взять, к примеру, следующую программу:
Эта программа является прозрачной. Я могу заменить один или оба вхождения
f()
с ,2
и она будет работать так же:или
или
все будут вести себя одинаково.
Ну, вообще-то, я обманул. Я должен иметь возможность заменить вызов на
print
его возвращаемое значение (которое вообще не имеет значения) без изменения смысла программы. Однако, ясно, что если я просто уберу дваprint
утверждения, смысл программы изменится: раньше она выводила что-то на экран, а после этого нет. Ввод / вывод не прозрачен.Простое эмпирическое правило: если вы можете заменить любое выражение, вызов под-выражения или вызов подпрограммы возвращаемым значением этого выражения, вызова под-выражения или вызова подпрограммы в любом месте программы, не изменяя его смысл в программе, то у вас есть ссылочный прозрачность. На практике это означает, что у вас не может быть никаких операций ввода-вывода, не может быть никакого изменяемого состояния, не может быть никаких побочных эффектов. В каждом выражении значение выражения должно зависеть исключительно от значений составных частей выражения. И в каждом вызове подпрограммы возвращаемое значение должно зависеть исключительно от аргументов.
источник
print
пример. Возможно, один из способов увидеть это состоит в том, что то, что напечатано на экране, является частью «возвращаемого значения». Если вы можете заменитьprint
его возвращаемым значением функции и эквивалентной записью на терминале, пример работает.4
и2 + 2
не взаимозаменяемыми, так как они имеют разное время выполнения, и весь смысл ссылочной прозрачности в том, что вы можете заменить выражение тем, к чему оно относится. Важным соображением будет безопасность потоков.listOfSequence.append(n)
возвращаетсяNone
, так что вы должны быть в состоянии заменить каждый вызовlistOfSequence.append(n)
сNone
без изменения смысла программы. Вы можете сделать это? Если нет, то он не является ссылочно прозрачным.Части этого ответа взяты из незавершенного учебника по функциональному программированию , размещенного на моей учетной записи GitHub:
Рассмотрим простой пример:
В чисто функциональном языке левая и правая части знака равенства могут заменять друг друга в обоих направлениях. То есть, в отличие от языка, подобного C, вышеприведенные обозначения действительно утверждают равенство. Следствием этого является то, что мы можем рассуждать о программном коде так же, как математические уравнения.
Из Haskell вики :
Чтобы контрастировать с этим, тип операции, выполняемой C-подобными языками, иногда называют деструктивным назначением .
Термин « чистый» часто используется для описания свойства выражений, относящихся к данному обсуждению. Чтобы функция считалась чистой,
Согласно метафоре черного ящика, найденной в многочисленных математических учебниках, внутренняя часть функции полностью изолирована от внешнего мира. Побочный эффект - это когда функция или выражение нарушают этот принцип, то есть процедуре разрешается каким-либо образом взаимодействовать с другими программными модулями (например, для обмена и обмена информацией).
Таким образом, ссылочная прозрачность необходима для того, чтобы функции вели себя как истинные , математические функции также в семантике языков программирования.
источник
[here](link to source)
...», а затем 2) правильное форматирование цитат (используйте кавычки, или еще лучше,>
символ). Также не помешало бы, если бы помимо общих указаний, отвечал на конкретные вопросы, о которых идет речь, в данном случаеf(x)+f(x)
/2*f(x)
, см. Как ответить - в противном случае может показаться, что вы просто рекламируете свою страницу