संदर्भात्मक पारदर्शिता क्या है?


38

मैंने देखा है कि अनिवार्य प्रतिमानों में

f (x) + f (x)

जैसा हो सकता है वैसा न हो:

2 * f (x)

लेकिन एक कार्यात्मक प्रतिमान में यह समान होना चाहिए। मैंने पायथन और स्कीम दोनों मामलों को लागू करने की कोशिश की है , लेकिन मेरे लिए वे बहुत ही सीधे हैं।

एक उदाहरण क्या होगा जो दिए गए फ़ंक्शन के साथ अंतर को इंगित कर सकता है?


7
आप अजगर में रेफरेंशियल ट्रांसपेरेंट फंक्शन्स लिख सकते हैं। अंतर यह है कि भाषा इसे लागू नहीं करती है।
कार्ल नेवलेफेल्ट

5
सी और एक जैसे: सी में समान f(x++)+f(x++)नहीं हो सकता है 2*f(x++)(सी में यह विशेष रूप से प्यारा है जब सामान मैक्रोज़ के भीतर छिपा हुआ है - क्या मैंने उस पर अपनी नाक तोड़ी थी? आपने शर्त लगाई)
gnat

मेरी समझ में, @ gnat का उदाहरण है कि आर जैसी कार्यात्मक रूप से उन्मुख भाषाएं पास-बाय-रेफरेंस को नियोजित करती हैं और अपने तर्कों को संशोधित करने वाले कार्यों से स्पष्ट रूप से बचती हैं। आर में, कम से कम, इन प्रतिबंधों को कम से कम करना मुश्किल हो सकता है (कम से कम, एक स्थिर, पोर्टेबल तरीके से) भाषा की जटिल प्रणाली के वातावरण और नामस्थान और खोज पथ में खुदाई के बिना।
छायाकार

4
@ssdecontrol: वास्तव में, जब आपके पास रेफरेन्शियल ट्रांसपेरेंसी होती है, तो पास-बाय-वैल्यू और पास-बाय-रेफरेंस हमेशा एक ही परिणाम देते हैं, इसलिए यह कोई फर्क नहीं पड़ता कि कौन सी भाषा का उपयोग करता है। कार्यात्मक भाषाओं को अक्सर अर्थ स्पष्टता के लिए पास-बाय-वैल्यू के लिए कुछ-कुछ के साथ निर्दिष्ट किया जाता है, लेकिन उनके कार्यान्वयन अक्सर प्रदर्शन के लिए पास-दर-संदर्भ का उपयोग करते हैं (या दोनों भी, जिसके आधार पर कोई दिए गए संदर्भ के लिए तेज़ है)।
जोर्ग डब्ल्यू मित्तग

4
@gnat: विशेष रूप से, f(x++)+f(x++)बिल्कुल कुछ भी हो सकता है, क्योंकि यह अपरिभाषित व्यवहार कर रहा है। लेकिन यह वास्तव में संदर्भित करने वाली पारदर्शिता से संबंधित नहीं है - जो इस कॉल के लिए मदद नहीं करेगा, यह संदर्भित रूप से पारदर्शी कार्यों के लिए भी अपरिभाषित sin(x++)+sin(x++)है। 42 हो सकता है, आपकी हार्ड ड्राइव को प्रारूपित कर सकता है, उपयोगकर्ताओं की नाक से बाहर उड़ने वाले राक्षस हो सकते हैं ...
क्रिस्टोफर क्रुट्ज़िग

जवाबों:


62

किसी फ़ंक्शन के लिए संदर्भित प्रासंगिक पारदर्शिता, यह इंगित करती है कि आप उस फ़ंक्शन को लागू करने का परिणाम केवल उसके तर्कों के मूल्यों को देखकर निर्धारित कर सकते हैं। आप किसी भी प्रोग्रामिंग भाषा, जैसे पायथन, स्कीम, पास्कल, सी में संदर्भात्मक पारदर्शी कार्य लिख सकते हैं।

दूसरी ओर, अधिकांश भाषाओं में आप गैर-संदर्भात्मक रूप से पारदर्शी कार्य भी लिख सकते हैं। उदाहरण के लिए, यह पायथन फ़ंक्शन:

counter = 0

def foo(x):
  global counter

  counter += 1
  return x + counter

वास्तव में पारदर्शी रूप से पारदर्शी नहीं है

foo(x) + foo(x)

तथा

2 * foo(x)

किसी भी तर्क के लिए विभिन्न मूल्यों का उत्पादन करेगा x। इसका कारण यह है कि फ़ंक्शन वैश्विक चर का उपयोग करता है और संशोधित करता है, इसलिए प्रत्येक आह्वान का परिणाम इस बदलती स्थिति पर निर्भर करता है, न कि केवल फ़ंक्शन के तर्क पर।

हस्केल, एक विशुद्ध रूप से कार्यात्मक भाषा, अभिव्यक्ति के मूल्यांकन को कड़ाई से अलग करती है जिसमें शुद्ध कार्य लागू होते हैं और जो हमेशा संदर्भित रूप से पारदर्शी होता है, एक्शन एक्जीक्यूशन (विशेष मानों का प्रसंस्करण) से, जो कि प्रासंगिक रूप से पारदर्शी नहीं होता है, यानी एक ही क्रिया को निष्पादित करने से हर बार हो सकता है अलग परिणाम।

तो, किसी भी हास्केल फ़ंक्शन के लिए

f :: Int -> Int

और कोई भी पूर्णांक x, यह हमेशा सच होता है

2 * (f x) == (f x) + (f x)

कार्रवाई का एक उदाहरण लाइब्रेरी फ़ंक्शन का परिणाम है getLine:

getLine :: IO String

अभिव्यक्ति मूल्यांकन के परिणामस्वरूप, यह फ़ंक्शन (वास्तव में एक स्थिर) सबसे पहले शुद्ध प्रकार का उत्पादन करता है IO String। इस प्रकार के मान किसी भी अन्य की तरह मान हैं: आप उन्हें पास कर सकते हैं, उन्हें डेटा संरचनाओं में डाल सकते हैं, उन्हें विशेष कार्यों का उपयोग कर रचना कर सकते हैं, और इसी तरह। उदाहरण के लिए आप क्रियाओं की सूची बना सकते हैं जैसे:

[getLine, getLine] :: [IO String]

कार्य विशेष हैं कि आप हास्केल रनटाइम को लिखकर उन्हें निष्पादित करने के लिए कह सकते हैं:

main = <some action>

इस मामले में, जब आपका हास्केल कार्यक्रम शुरू किया जाता है, तो रनटाइम उस क्रिया के माध्यम से चलता है mainऔर इसे निष्पादित करता है, संभवतः साइड-इफेक्ट्स का उत्पादन करता है। इसलिए, एक्शन निष्पादन संदर्भित रूप से पारदर्शी नहीं है क्योंकि एक ही एक्शन को दो बार निष्पादित करने से रनटाइम इनपुट के रूप में प्राप्त होने के आधार पर विभिन्न परिणाम उत्पन्न कर सकते हैं।

हास्केल के प्रकार प्रणाली के लिए धन्यवाद, एक कार्रवाई का उपयोग उस संदर्भ में कभी नहीं किया जा सकता है जहां एक अन्य प्रकार की अपेक्षा की जाती है, और इसके विपरीत। इसलिए, यदि आप एक स्ट्रिंग की लंबाई का पता लगाना चाहते हैं, तो आप lengthफ़ंक्शन का उपयोग कर सकते हैं :

length "Hello"

5. वापस आ जाएगा। लेकिन अगर आप टर्मिनल से पढ़े गए स्ट्रिंग की लंबाई का पता लगाना चाहते हैं, तो आप नहीं लिख सकते

length (getLine)

क्योंकि आपको एक प्रकार की त्रुटि मिलती है: lengthएक प्रकार की सूची (और एक स्ट्रिंग, वास्तव में, एक सूची) के इनपुट की अपेक्षा करता है, लेकिन getLineप्रकार IO String(एक क्रिया) का एक मूल्य है । इस प्रकार, टाइप सिस्टम यह सुनिश्चित करता है कि एक एक्शन वैल्यू जैसे getLine(जिसका निष्पादन मूल भाषा के बाहर किया गया हो और जो गैर-संदर्भात्मक रूप से पारदर्शी हो) गैर-एक्शन मूल्य के अंदर छिपाया नहीं जा सकता है Int

संपादित करें

एक्साइज सवाल का जवाब देने के लिए, यहां एक छोटा हास्केल प्रोग्राम है जो कंसोल से एक लाइन पढ़ता है और इसकी लंबाई प्रिंट करता है।

main :: IO () -- The main program is an action of type IO ()
main = do
          line <- getLine
          putStrLn (show (length line))

मुख्य क्रिया में दो सबऑउट होते हैं जिन्हें क्रमिक रूप से निष्पादित किया जाता है:

  1. getlineप्रकार के IO String,
  2. दूसरा इसके तर्क पर putStrLnप्रकार के कार्य का मूल्यांकन करके बनाया गया String -> IO ()है।

अधिक सटीक रूप से, दूसरी क्रिया द्वारा निर्मित है

  1. lineपहली कार्रवाई द्वारा पढ़े गए मूल्य के लिए बाध्यकारी ,
  2. शुद्ध कार्यों का मूल्यांकन length(पूर्णांक के रूप में लंबाई की गणना) और फिर show(पूर्णांक में पूर्णांक को मोड़ें),
  3. putStrLnके परिणाम के लिए समारोह लागू करके कार्रवाई का निर्माण show

इस बिंदु पर, दूसरी कार्रवाई निष्पादित की जा सकती है। यदि आपने "हैलो" टाइप किया है, तो यह "5" प्रिंट करेगा।

ध्यान दें कि यदि आपको <-नोटेशन का उपयोग करके किसी कार्रवाई से कोई मूल्य मिलता है, तो आप केवल उस मूल्य का उपयोग किसी अन्य कार्रवाई के अंदर कर सकते हैं, जैसे कि आप नहीं लिख सकते हैं:

main = do
          line <- getLine
          show (length line) -- Error:
                             -- Expected type: IO ()
                             --   Actual type: String

क्योंकि show (length line)टाइप है Stringजबकि do notation के लिए आवश्यक है कि एक एक्शन ( getLineप्रकार का IO String) दूसरे एक्शन (उदाहरण putStrLn (show (length line))के प्रकार IO ()) के बाद हो।

EDIT 2

जार्ग डब्ल्यू मित्तग की संदर्भात्मक पारदर्शिता की परिभाषा मेरी तुलना में अधिक सामान्य है (मैंने उनके जवाब को गलत ठहराया है)। मैंने एक प्रतिबंधित परिभाषा का उपयोग किया है क्योंकि प्रश्न में उदाहरण कार्यों के वापसी मूल्य पर केंद्रित है और मैं इस पहलू को चित्रित करना चाहता था। हालांकि, सामान्य रूप से आरटी पूरे कार्यक्रम के अर्थ को संदर्भित करता है, जिसमें वैश्विक स्थिति में बदलाव और एक अभिव्यक्ति का मूल्यांकन करने के कारण पर्यावरण (आईओ) के साथ बातचीत शामिल है। तो, एक सही, सामान्य परिभाषा के लिए, आपको उस उत्तर का उल्लेख करना चाहिए।


10
क्या डाउनवॉटर सुझाव दे सकता है कि मैं इस उत्तर को कैसे सुधार सकता हूं?
जियोर्जियो

तो हास्केल में टर्मिनल से पढ़ी जाने वाली स्ट्रिंग की लंबाई कैसे होगी?
sbichenko

2
यह अत्यंत पांडित्यपूर्ण है, लेकिन पूर्णता के लिए, यह हास्केल की प्रकार प्रणाली नहीं है जो क्रियाओं और शुद्ध कार्यों का मिश्रण नहीं करती है; यह तथ्य है कि भाषा कोई भी अशुद्ध कार्य प्रदान नहीं करती है जिसे आप सीधे कॉल कर सकते हैं। आप वास्तव में हंबल के IOप्रकार को किसी भी भाषा में लैम्ब्डा और जेनेरिक के साथ आसानी से लागू कर सकते हैं , लेकिन क्योंकि कोई भी printlnसीधे कॉल कर सकता है , कार्यान्वयन IOशुद्धता की गारंटी नहीं देता है; यह केवल एक सम्मेलन होगा।
डोवाल

मेरा मतलब था कि (१) सभी कार्य शुद्ध हैं (निश्चित रूप से, वे शुद्ध हैं क्योंकि भाषा किसी भी अशुद्ध लोगों को प्रदान नहीं करती है, भले ही मुझे पता है कि वहाँ कुछ तंत्र हैं कि बाईपास करने के लिए), और (२) शुद्ध कार्य और अशुद्ध कार्यों के विभिन्न प्रकार होते हैं, इसलिए उन्हें मिश्रित नहीं किया जा सकता है। BTW, आप सीधे कॉल से क्या मतलब है ?
जियोर्जियो

6
getLineसंदर्भात्मक रूप से पारदर्शी नहीं होने के बारे में आपकी बात गलत है। आप प्रस्तुत कर रहे हैं getLineजैसे कि यह कुछ स्ट्रिंग का मूल्यांकन करता है या कम करता है, जिसमें से विशेष स्ट्रिंग उपयोगकर्ता के इनपुट पर निर्भर करता है। यह गलत है। IO Stringकिसी स्ट्रिंग को इससे अधिक Maybe Stringनहीं करता है। IO Stringके लिए एक नुस्खा है, संभवतः एक स्ट्रिंग प्राप्त करने और, एक अभिव्यक्ति के रूप में, यह हास्केल में किसी भी अन्य के रूप में शुद्ध है।
लक्जमोड

25
def f(x): return x()

from random import random
f(random) + f(random) == 2*f(random)
# => False

हालाँकि, यह रेफ़रेंशियल ट्रांसपेरेंसी का मतलब नहीं है। आरटी का अर्थ है कि आप कार्यक्रम के अर्थ को बदले बिना उस अभिव्यक्ति (या इसके विपरीत) के मूल्यांकन के परिणाम के साथ कार्यक्रम में किसी भी अभिव्यक्ति को बदल सकते हैं ।

उदाहरण के लिए, निम्नलिखित कार्यक्रम लें:

def f(): return 2

print(f() + f())
print(2)

यह कार्यक्रम प्रासंगिक रूप से पारदर्शी है। मैं इनमें से एक या दोनों आवृत्तियां जगह ले सकता है f()के साथ 2और यह अभी भी एक ही काम करेंगे:

def f(): return 2

print(2 + f())
print(2)

या

def f(): return 2

print(f() + 2)
print(2)

या

def f(): return 2

print(2 + 2)
print(f())

क्या सभी एक जैसा व्यवहार करेंगे।

खैर, वास्तव में, मैंने धोखा दिया। मुझे printप्रोग्राम के अर्थ को बदले बिना कॉल को इसके वापसी मूल्य (जो कि बिल्कुल भी कोई मूल्य नहीं है) के साथ बदलने में सक्षम होना चाहिए । हालांकि, स्पष्ट रूप से, अगर मैं सिर्फ दो printबयानों को हटा देता हूं, तो कार्यक्रम का अर्थ बदल जाएगा: पहले, यह स्क्रीन पर कुछ मुद्रित करता है, इसके बाद नहीं। I / O संदर्भित रूप से पारदर्शी नहीं है।

अंगूठे का सरल नियम है: यदि आप किसी भी अभिव्यक्ति, उप-अभिव्यक्ति या सबरूटीन कॉल को उस अभिव्यक्ति के उप मान के साथ बदल सकते हैं, तो सब-एक्सप्रेशन या सबरूटीन कॉल प्रोग्राम में कहीं भी हो, प्रोग्राम के बिना इसका अर्थ बदले में, तो आपके पास संदर्भात्मक है पारदर्शिता। और इसका अर्थ यह है कि व्यावहारिक रूप से यह कहा जा सकता है कि आपके पास कोई I / O नहीं हो सकता है, कोई भी परस्पर स्थिति नहीं हो सकती है, कोई दुष्प्रभाव नहीं हो सकता है। प्रत्येक अभिव्यक्ति में, अभिव्यक्ति का मूल्य पूरी तरह से अभिव्यक्ति के घटक भागों के मूल्यों पर निर्भर होना चाहिए। और हर सबरूटीन कॉल में, रिटर्न वैल्यू केवल तर्कों पर निर्भर होना चाहिए।


4
"कोई भी परिवर्तनशील स्थिति नहीं हो सकती": ठीक है, आपके पास यह हो सकता है यदि यह छिपा हुआ है और आपके कोड के अवलोकन योग्य व्यवहार को प्रभावित नहीं करता है। संस्मरण के बारे में सोचो।
जियोर्जियो

4
@Giorgio: यह शायद व्यक्तिपरक है, लेकिन मैं तर्क दूंगा कि कैश्ड परिणाम वास्तव में "उत्परिवर्तनीय स्थिति" नहीं हैं यदि वे छिपे हुए हैं और कोई अवलोकन प्रभाव नहीं है। अपरिवर्तनीयता हमेशा उत्परिवर्तनीय हार्डवेयर के शीर्ष पर लागू की गई अमूर्तता है; अक्सर यह भाषा द्वारा प्रदान किया जाता है (निष्पादन के दौरान रजिस्टरों और मेमोरी स्थानों के बीच मूल्य बदल सकता है और भले ही इसे फिर से उपयोग नहीं किया जाएगा) गायब हो सकता है, लेकिन "यह मूल्य फिर से उपयोग नहीं किया जा सकता है", भले ही यह मान्य नहीं है। एक पुस्तकालय या whatnot द्वारा प्रदान की गई। (यह मानते हुए कि यह सही ढंग से लागू किया गया है।)
23

1
+1 मुझे वास्तव में printउदाहरण पसंद है । शायद इसे देखने का एक तरीका यह है कि स्क्रीन पर जो छपा है वह "रिटर्न वैल्यू" का हिस्सा है। यदि आप printइसके फ़ंक्शन रिटर्न मान और टर्मिनल पर समकक्ष लेखन के साथ बदल सकते हैं , तो उदाहरण काम करता है।
पियरे अरलाउड

1
@ जियोर्जियो स्पेस / समय के उपयोग को संदर्भात्मक पारदर्शिता के उद्देश्यों के लिए एक साइड इफेक्ट नहीं माना जा सकता है। यह अलग-अलग चलने के समय है, 4और 2 + 2गैर-विनिमेय बना देगा , और संदर्भित पारदर्शिता की पूरी बात यह है कि आप जो कुछ भी इसका मूल्यांकन करते हैं, उसके साथ एक अभिव्यक्ति को प्रतिस्थापित कर सकते हैं। महत्वपूर्ण विचार धागा सुरक्षा होगा।
डोभाल

1
@overexchange: रेफ़रेंशियल ट्रांसपेरेंसी का मतलब है कि आप प्रोग्राम के अर्थ को बदले बिना हर सबप्रेशन को उसके मूल्य से बदल सकते हैं। listOfSequence.append(n)रिटर्न None, तो आप के लिए हर कॉल को बदलने के लिए सक्षम होना चाहिए listOfSequence.append(n)के साथ Noneअपने कार्यक्रम के अर्थ को बदले बिना। क्या आप यह कर सकते हैं? यदि नहीं, तो यह प्रासंगिक रूप से पारदर्शी नहीं है।
जोर्ग डब्ल्यू मित्तग

1

इस जवाब के कुछ हिस्सों को सीधे कार्यात्मक प्रोग्रामिंग पर एक अधूरे ट्यूटोरियल से लिया गया है , जिसे मेरे GitHub खाते में होस्ट किया गया है:

एक फ़ंक्शन को संदर्भात्मक रूप से पारदर्शी कहा जाता है यदि यह एक ही इनपुट मापदंडों को देखते हुए, हमेशा एक ही आउटपुट (रिटर्न वैल्यू) पैदा करता है। यदि कोई शुद्ध कार्यात्मक प्रोग्रामिंग के लिए एक raison d'être की तलाश कर रहा है, तो संदर्भात्मक पारदर्शिता एक अच्छा उम्मीदवार है। जब बीजगणित, अंकगणित और तर्क में सूत्र के साथ तर्क करते हैं, तो यह संपत्ति - जिसे समान के लिए बराबरी की विशिष्टता भी कहा जाता है - इतना मौलिक रूप से महत्वपूर्ण है कि इसे आमतौर पर लिया जाता है ...

एक साधारण उदाहरण पर विचार करें:

x = 42

एक शुद्ध कार्यात्मक भाषा में, बराबरी के चिह्न के बाएँ हाथ और दाएँ हाथ एक दूसरे के लिए दोनों तरह से प्रतिस्थापित कर सकते हैं। यही है, सी जैसी भाषा के विपरीत, उपरोक्त संकेतन वास्तव में एक समानता का दावा करता है। इसका परिणाम यह है कि हम गणितीय समीकरणों की तरह ही प्रोग्राम कोड के बारे में तर्क कर सकते हैं।

से हास्केल विकी :

शुद्ध संगणनाओं को हर बार उतने ही मूल्य पर उतारा जाता है। इस संपत्ति को रेफ़रेंशियल ट्रांसपेरेंसी कहा जाता है और कोड पर समान तर्क का संचालन करना संभव बनाता है ...

इसके विपरीत, सी-जैसी भाषाओं द्वारा किए गए ऑपरेशन के प्रकार को कभी-कभी विनाशकारी असाइनमेंट के रूप में संदर्भित किया जाता है ।

शुद्ध शब्द का उपयोग अक्सर अभिव्यक्ति की एक संपत्ति का वर्णन करने के लिए किया जाता है, जो इस चर्चा के लिए प्रासंगिक है। किसी कार्य को शुद्ध मानने के लिए,

  • इसे किसी भी दुष्प्रभाव को प्रदर्शित करने की अनुमति नहीं है, और
  • यह प्रासंगिक रूप से पारदर्शी होना चाहिए।

कई गणितीय पाठ्यपुस्तकों में पाए गए ब्लैक-बॉक्स रूपक के अनुसार, एक फ़ंक्शन के इंटर्नल को बाहरी दुनिया से पूरी तरह से बंद कर दिया जाता है। एक साइड-इफ़ेक्ट तब होता है जब कोई फ़ंक्शन या एक्सप्रेशन इस सिद्धांत का उल्लंघन करता है - अर्थात, प्रक्रिया को अन्य प्रोग्राम यूनिट्स के साथ किसी तरह से संचार करने की अनुमति दी जाती है (उदाहरण साझा करने और जानकारी का आदान-प्रदान करने के लिए)।

सारांश में, संदर्भात्मक पारदर्शिता कार्यों के लिए एक सही , गणितीय कार्यों जैसे प्रोग्रामिंग भाषाओं के शब्दार्थ में भी होना चाहिए ।


ऐसा लगता है कि शब्द-दर-शब्द कॉपी के साथ यहाँ से खोला गया है : "एक फ़ंक्शन को संदर्भित रूप से पारदर्शी कहा जाता है यदि यह एक ही इनपुट मापदंडों को देखते हुए, हमेशा एक ही आउटपुट का उत्पादन करता है ..." स्टैक एक्सचेंज में साहित्यिक चोरी के नियम हैं, आप इन के बारे में जानते हैं? "साहित्यिक चोरी किसी और के काम की नकल करने, उस पर अपना नाम उछालने और खुद को मूल लेखक के रूप में पारित करने की
स्मृतिहीनता है

3
मैंने वह पेज लिखा।
येतिसुसर

यदि यह मामला है, तो इसे एक साहित्यिक चोरी से कम करने पर विचार करें - क्योंकि पाठकों के पास बताने का कोई तरीका नहीं है। क्या आप जानते हैं कि एसई में यह कैसे करना है? 1) आप मूल स्रोत का उल्लेख करते हैं, जैसे "जैसा (मेरे पास) लिखा है [here](link to source)..." 2 के बाद) उचित उद्धरण स्वरूपण (उद्धरण चिह्नों का उपयोग करें, या बेहतर अभी तक, उसके लिए > प्रतीक)। यदि सामान्य मार्गदर्शन देने के अलावा, इस बारे में पूछे गए ठोस सवाल, f(x)+f(x)/ के बारे में इस मामले में 2*f(x)देखें, तो इसका उत्तर कैसे देखें - यह भी चोट नहीं पहुंचाएगा, अन्यथा ऐसा लग सकता है कि आप बस अपने पृष्ठ का विज्ञापन कर रहे हैं
gnat

1
सैद्धांतिक रूप से, मुझे यह उत्तर समझ में आया। लेकिन, व्यावहारिक रूप से इन नियमों का पालन करते हुए, मुझे इस कार्यक्रम में हेलस्टोन अनुक्रम सूची वापस करने की आवश्यकता है । मैं यह कैसे करु?
ओवरएक्सचेंज
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.