आप उत्परिवर्तित स्थिति के बिना कुछ भी उपयोगी कैसे कर सकते हैं?


265

मैं हाल ही में कार्यात्मक प्रोग्रामिंग के बारे में बहुत सारे सामान पढ़ रहा हूं, और मैं इसे सबसे अधिक समझ सकता हूं, लेकिन एक चीज जिसे मैं अपने सिर के चारों ओर लपेट नहीं सकता हूं, वह स्टेटलेस कोडिंग है। मुझे ऐसा लगता है कि परिवर्तनशील स्थिति को हटाकर प्रोग्रामिंग को सरल बनाना डैशबोर्ड को हटाकर एक कार को "सरल" करने जैसा है: तैयार उत्पाद सरल हो सकता है, लेकिन सौभाग्य यह अंत उपयोगकर्ताओं के साथ बातचीत करता है।

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

हर बार जब मुझे कुछ मिलता है जो इस मुद्दे पर चर्चा करता है, तो यह वास्तव में तकनीकी कार्यात्मक-एईएस में लिखा है जो एक भारी एफपी पृष्ठभूमि को मानता है जो मेरे पास नहीं है। क्या किसी को अनिवार्य कोडिंग की अच्छी, ठोस समझ के साथ किसी को यह समझाने का तरीका पता है लेकिन कार्यात्मक पक्ष पर पूर्ण n00b कौन है?

संपादित करें: उत्तरों का एक गुच्छा अब तक मुझे अपरिवर्तनीय मूल्यों के लाभों को समझाने की कोशिश कर रहा है। मुझे वह हिस्सा मिलता है। यह सही समझ में आता है। मुझे समझ में नहीं आता है कि आप किस तरह से उन मूल्यों का ट्रैक रख सकते हैं जिन्हें परिवर्तन करना है, और लगातार बदलना है, बिना परिवर्तनशील चर के।



1
मेरी निजी विनम्र राय है कि यह ताकत और पैसे की तरह है। कम रिटर्न का कानून लागू होता है। यदि आप काफी मजबूत हैं, तो थोड़ा मजबूत होने के लिए थोड़ा प्रोत्साहन मिल सकता है, लेकिन इस पर काम करने के लिए चोट नहीं लगती है (और कुछ लोग जुनून के साथ करते हैं)। यही बात वैश्विक परिवर्तनशील स्थिति पर भी लागू होती है। यह स्वीकार करना मेरी व्यक्तिगत प्राथमिकता है कि जैसे-जैसे मेरी कोडिंग स्किल आगे बढ़ती है, मेरे कोड में ग्लोबल म्यूटेबल स्टेट की मात्रा को सीमित करना अच्छा होता है। यह कभी भी पूर्ण नहीं हो सकता है लेकिन वैश्विक परिवर्तनशील स्थिति को कम करने की दिशा में काम करना अच्छा है।
AturSams 16

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

7
संक्षेप में, एफपी में, फ़ंक्शन राज्य को कभी भी संशोधित नहीं करते हैं। आखिरकार वे कुछ ऐसा करेंगे जो वर्तमान स्थिति को बदल देता है। लेकिन राज्य कभी भी संशोधित (उत्परिवर्तित) नहीं होता है।
जिंगलेस्टुला

म्यूटेशन के बिना स्थिति को प्राप्त करने के तरीके हैं (जो मैं समझता हूं उससे स्टैक का उपयोग कर रहा हूं), लेकिन यह प्रश्न बिंदु के बगल में है (भले ही यह एक महान हो)। सख़्ती से बात करना मुश्किल है, लेकिन यहाँ एक पोस्ट है जो उम्मीद करता है कि आपके प्रश्न का उत्तर होगा मध्यम . com/@jbmilgrom/… । TLDR यह है कि एक राज्य के कार्यात्मक कार्यक्रम के शब्दार्थ भी अपरिवर्तनीय हैं, हालाँकि प्रोग्राम फ़ंक्शन के संचार b / w रन को नियंत्रित किया जाता है।
jbmilgrom

जवाबों:


166

या अगर आप एक वीडियो गेम खेलते हैं, तो सभी पात्रों के पदों के साथ शुरू होने वाले, कई राज्य चर हैं, जो लगातार घूमते रहते हैं। बदलते मूल्यों पर नज़र रखे बिना आप संभवतः कैसे उपयोगी कुछ कर सकते हैं?

यदि आप रुचि रखते हैं, तो यहां उन लेखों की एक श्रृंखला है जो Erlang के साथ गेम प्रोग्रामिंग का वर्णन करते हैं।

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

मुझे पहली बार में संदेह हुआ, लेकिन मैं कुछ साल पहले कार्यात्मक प्रोग्रामिंग ट्रेन में कूद गया और इसके साथ प्यार हो गया। कार्यात्मक प्रोग्रामिंग के साथ चाल पैटर्न को पहचानने में सक्षम हो रही है, विशेष रूप से चर असाइनमेंट, और आवश्यक स्थिति को स्टैक पर ले जाने के लिए। उदाहरण के लिए, लूप, पुनरावर्तन बन जाता है:

// Imperative
let printTo x =
    for a in 1 .. x do
        printfn "%i" a

// Recursive
let printTo x =
    let rec loop a = if a <= x then printfn "%i" a; loop (a + 1)
    loop 1

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

// Preferred
let printTo x = seq { 1 .. x } |> Seq.iter (fun a -> printfn "%i" a)

Seq.iter विधि संग्रह के माध्यम से गणना करेगा और प्रत्येक आइटम के लिए अनाम फ़ंक्शन को आमंत्रित करेगा। बेहद सुविधाजनक :)

मुझे पता है, मुद्रण संख्या बिल्कुल प्रभावशाली नहीं है। हालांकि, हम गेम के साथ एक ही दृष्टिकोण का उपयोग कर सकते हैं: स्टैक में सभी स्थिति को पकड़ो और पुनरावर्ती कॉल में हमारे परिवर्तनों के साथ एक नई वस्तु बनाएं। इस तरह, प्रत्येक फ्रेम गेम का एक स्टेटलेस स्नैपशॉट है, जहां प्रत्येक फ्रेम बस एक नई वस्तु बनाता है, जिसे स्टेटलेस ऑब्जेक्ट्स को अपडेट करने की आवश्यकता होती है। इसके लिए छद्म कोड हो सकता है:

// imperative version
pacman = new pacman(0, 0)
while true
    if key = UP then pacman.y++
    elif key = DOWN then pacman.y--
    elif key = LEFT then pacman.x--
    elif key = UP then pacman.x++
    render(pacman)

// functional version
let rec loop pacman =
    render(pacman)
    let x, y = switch(key)
        case LEFT: pacman.x - 1, pacman.y
        case RIGHT: pacman.x + 1, pacman.y
        case UP: pacman.x, pacman.y - 1
        case DOWN: pacman.x, pacman.y + 1
    loop(new pacman(x, y))

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

यह खेल में किसी भी संख्या तक वस्तुओं को मापता है, क्योंकि सभी वस्तुओं (या संबंधित वस्तुओं के संग्रह) को अपने स्वयं के धागे में प्रस्तुत किया जा सकता है।

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

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

using System;

namespace ConsoleApplication1
{
    static class Stack
    {
        public static Stack<T> Cons<T>(T hd, Stack<T> tl) { return new Stack<T>(hd, tl); }
        public static Stack<T> Append<T>(Stack<T> x, Stack<T> y)
        {
            return x == null ? y : Cons(x.Head, Append(x.Tail, y));
        }
        public static void Iter<T>(Stack<T> x, Action<T> f) { if (x != null) { f(x.Head); Iter(x.Tail, f); } }
    }

    class Stack<T>
    {
        public readonly T Head;
        public readonly Stack<T> Tail;
        public Stack(T hd, Stack<T> tl)
        {
            this.Head = hd;
            this.Tail = tl;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Stack<int> x = Stack.Cons(1, Stack.Cons(2, Stack.Cons(3, Stack.Cons(4, null))));
            Stack<int> y = Stack.Cons(5, Stack.Cons(6, Stack.Cons(7, Stack.Cons(8, null))));
            Stack<int> z = Stack.Append(x, y);
            Stack.Iter(z, a => Console.WriteLine(a));
            Console.ReadKey(true);
        }
    }
}

उपरोक्त कोड दो अपरिवर्तनीय सूचियों का निर्माण करता है, उन्हें एक नई सूची बनाने के लिए एक साथ जोड़ता है, और परिणामों को जोड़ता है। आवेदन में कहीं भी कोई परिवर्तनशील स्थिति का उपयोग नहीं किया गया है। यह थोड़ा भारी लगता है, लेकिन यह केवल इसलिए है क्योंकि C # एक मौखिक भाषा है। यहाँ F # में समान कार्यक्रम है:

type 'a stack =
    | Cons of 'a * 'a stack
    | Nil

let rec append x y =
    match x with
    | Cons(hd, tl) -> Cons(hd, append tl y)
    | Nil -> y

let rec iter f = function
    | Cons(hd, tl) -> f(hd); iter f tl
    | Nil -> ()

let x = Cons(1, Cons(2, Cons(3, Cons(4, Nil))))
let y = Cons(5, Cons(6, Cons(7, Cons(8, Nil))))
let z = append x y
iter (fun a -> printfn "%i" a) z

सूचियों को बनाने और हेरफेर करने के लिए कोई भी परिवर्तनशील आवश्यक नहीं है। लगभग सभी डेटा संरचनाओं को उनके कार्यात्मक समकक्षों में आसानी से परिवर्तित किया जा सकता है। मैंने यहां एक पृष्ठ लिखा है जो ढेर, कतार, वामपंथी ढेर, लाल-काले पेड़, आलसी सूची के अपरिवर्तनीय कार्यान्वयन प्रदान करता है। कोड के एक भी स्निपेट में कोई भी परिवर्तनशील स्थिति नहीं होती है। एक पेड़ को "म्यूट" करने के लिए, मैं नया नोड चाहता हूं, जिसके साथ मैं चाहता हूं - यह बहुत ही कुशल है क्योंकि मुझे पेड़ में हर नोड की एक प्रति बनाने की आवश्यकता नहीं है, मैं अपने नए में पुराने का पुन: उपयोग कर सकता हूं पेड़।

अधिक महत्वपूर्ण उदाहरण का उपयोग करते हुए, मैंने यह SQL पार्सर भी लिखा है जो पूरी तरह से स्टेटलेस है (या कम से कम मेरा कोड स्टेटलेस है, मुझे नहीं पता कि अंतर्निहित लेक्सिंग लाइब्रेरी स्टेटलेस है)।

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


7
मुझे पैक्मैन उदाहरण पसंद है। लेकिन यह एक समस्या को केवल दूसरे को उठाने के लिए हल कर सकता है: क्या होगा यदि कोई चीज मौजूदा Pacman ऑब्जेक्ट का संदर्भ रखती है? फिर इसे कचरा-एकत्र और प्रतिस्थापित नहीं किया जाएगा; इसके बजाय आप ऑब्जेक्ट की दो प्रतियों के साथ समाप्त होते हैं, जिनमें से एक अमान्य है। आप इस समस्या को कैसे संभालेंगे?
मेसन व्हीलर

9
जाहिर है कि आपको नई Pacman ऑब्जेक्ट के साथ एक नया "कुछ और" बनाने की आवश्यकता है;) बेशक, अगर हम उस मार्ग को बहुत दूर ले जाते हैं, तो हम अपने पूरे विश्व के लिए ऑब्जेक्ट ग्राफ को फिर से बनाते हुए कुछ बदलाव करते हैं। एक बेहतर दृष्टिकोण यहाँ वर्णित है ( prog21.dadgum.com/26.html ): वस्तुओं के बजाय खुद को और अपने सभी निर्भरता को अद्यतन करने के बजाय, इसके लिए उन्हें अपने राज्य के बारे में संदेश पास करने के लिए बहुत आसान है एक इवेंट लूप जो सभी को संभालता है अद्यतन। इससे यह तय करना बहुत आसान हो जाता है कि ग्राफ़ में किन वस्तुओं को अपडेट करने की आवश्यकता है, और कौन सी नहीं।
जूलियट

6
@ जूलियट, मुझे एक संदेह है - मेरी पूरी तरह से अनिवार्य मानसिकता में, कुछ बिंदु पर पुनरावृत्ति समाप्त होनी चाहिए, अन्यथा आप अंततः एक ढेर अतिप्रवाह पैदा करेंगे। पुनरावर्ती पेसमैन उदाहरण में, खाड़ी में रखा गया स्टैक कैसा है - क्या फ़ंक्शन की शुरुआत में ऑब्जेक्ट अनुमानित रूप से पॉपअप होता है?
ब्लूस्ट्रैट

9
@BlueStrat - अच्छा सवाल ... अगर यह एक "पूंछ कॉल" है ... यानी पुनरावर्ती कॉल फ़ंक्शन में अंतिम चीज है ... तो सिस्टम को एक नया स्टैक फ्रेम उत्पन्न करने की आवश्यकता नहीं है ... यह कर सकता है बस पिछले एक का पुन: उपयोग करें। यह कार्यात्मक प्रोग्रामिंग भाषाओं के लिए एक सामान्य अनुकूलन है। en.wikipedia.org/wiki/Tail_call
रेकप्टिलियन

4
@MichaelOsofsky, जब डेटाबेस और एपीआई के साथ बातचीत करते हैं, तो हमेशा एक 'बाहरी दुनिया' होती है जिसके साथ संवाद करने के लिए राज्य होता है। इस मामले में, आप 100% कार्यात्मक नहीं जा सकते। इस unct अनफेशनल ’कोड को अलग-थलग और सारगर्भित रखना ज़रूरी है इसलिए बाहरी दुनिया में केवल एक ही प्रवेश और एक निकास है। इस तरह से आप अपने बाकी कोड को कार्यात्मक रख सकते हैं।
चिलेट

76

संक्षिप्त उत्तर: आप नहीं कर सकते।

तो फिर अस्थिरता के बारे में उपद्रव क्या है?

यदि आप अनिवार्य भाषा के अच्छे जानकार हैं, तो आप जानते हैं कि "ग्लोबल्स खराब हैं"। क्यों? क्योंकि वे आपके कोड में कुछ बहुत ही कठिन-से-निर्भर निर्भरता का परिचय देते हैं (या परिचय करने की क्षमता रखते हैं)। और निर्भरताएं अच्छी नहीं हैं; आप चाहते हैं कि आपका कोड मॉड्यूलर हो । कार्यक्रम के हिस्से अन्य भागों को यथासंभव कम प्रभावित नहीं करते हैं। और एफपी प्रतिरूपकता की ही सर्वोपरि पर ले आता है: कोई साइड इफेक्ट बिल्कुल । आपके पास बस आपका f (x) = y है। X लगाओ, बाहर निकालो। एक्स या कुछ और के लिए कोई परिवर्तन नहीं। एफपी आपको राज्य के बारे में सोचना बंद कर देता है, और मूल्यों के संदर्भ में सोचना शुरू कर देता है। आपके सभी कार्य केवल मान प्राप्त करते हैं और नए मान उत्पन्न करते हैं।

इसके कई फायदे हैं।

सबसे पहले, कोई साइड-इफेक्ट का मतलब सरल कार्यक्रम नहीं है, इसके बारे में तर्क करना आसान है। कोई चिंता की बात नहीं है कि कार्यक्रम के एक नए हिस्से को पेश करने के लिए एक मौजूदा, काम कर रहे भाग को बाधित और क्रैश करने जा रहा है।

दूसरा, यह प्रोग्राम को तुच्छ रूप से समानांतर बनाता है (कुशल समानांतरकरण एक और मामला है)।

तीसरा, कुछ संभावित प्रदर्शन लाभ हैं। कहो कि आपके पास एक समारोह है:

double x = 2 * x

अब आप 3 के मान में रखते हैं, और आपको 6 में से एक मान मिलता है। हर बार। लेकिन आप ऐसा कर सकते हैं और साथ ही जरूरी है? हां। लेकिन समस्या यह है कि अनिवार्यता में, आप और भी अधिक कर सकते हैं । मैं कर सकता हूँ:

int y = 2;
int double(x){ return x * y; }

लेकिन मैं भी कर सकता था

int y = 2;
int double(x){ return x * (y++); }

अनिवार्य कंपाइलर को पता नहीं है कि मुझे साइड इफेक्ट्स होने हैं या नहीं, जो कि ऑप्टिमाइज़ करना अधिक कठिन है (यानी डबल 2 की जरूरत हर बार 4 नहीं होगी)। कार्यात्मक व्यक्ति जानता है कि मैं नहीं करूंगा - इसलिए, यह हर बार "डबल 2" को देखने का अनुकूलन कर सकता है।

अब, भले ही हर बार नए मूल्यों का निर्माण कंप्यूटर मेमोरी के संदर्भ में जटिल प्रकार के मूल्यों के लिए अविश्वसनीय रूप से बेकार लगता है, लेकिन ऐसा होना जरूरी नहीं है। क्योंकि, यदि आपके पास f (x) = y, और मान x और y "अधिकतर समान हैं" (जैसे कि पेड़ जो केवल कुछ लीफ़्स में भिन्न होते हैं) तो x और y स्मृति के कुछ हिस्सों को साझा कर सकते हैं - क्योंकि उनमें से कोई भी उत्परिवर्तित नहीं होगा। ।

तो अगर यह अचूक बात इतनी महान है, तो मैंने क्यों जवाब दिया कि आप बिना उत्परिवर्ती राज्य के लिए कुछ भी उपयोगी नहीं कर सकते। ठीक है, बिना परिवर्तनशीलता के, आपका पूरा कार्यक्रम एक विशाल च (x) = y फ़ंक्शन होगा। और वही आपके कार्यक्रम के सभी हिस्सों के लिए जाएगा: बस कार्य, और उस पर "शुद्ध" अर्थ में कार्य करता है। जैसा कि मैंने कहा, इसका मतलब है हर बार f (x) = y । इसलिए उदाहरण के लिए readFile ("myFile.txt") को हर बार समान स्ट्रिंग मान वापस करने की आवश्यकता होगी। बहुत उपयोगी नहीं है।

इसलिए, हर FP राज्य को बदलने के कुछ साधन प्रदान करता है । "शुद्ध" कार्यात्मक भाषाएं (जैसे हास्केल) कुछ डरावनी अवधारणाओं जैसे कि मोनड्स का उपयोग करके ऐसा करती हैं, जबकि "अशुद्ध" वाले (जैसे एमएल) इसे सीधे अनुमति देते हैं।

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


2
<< readFile ("myFile.txt") को हर बार एक ही स्ट्रिंग मान वापस करने की आवश्यकता होगी। बहुत उपयोगी नहीं है। >> मैं अनुमान लगा रहा हूं कि यह तब तक उपयोगी है, जब तक आप वैश्विक, एक फाइल सिस्टम को छिपाते हैं। यदि आप इसे दूसरे पैरामीटर के रूप में मानते हैं और अन्य प्रक्रियाओं को फाइलसिस्टम के लिए एक नया संदर्भ लौटाने देते हैं, तो हर बार जब वे इसे फाइलसिस्टम 2 के साथ संशोधित करते हैं = (filesystem1, fd, pos, "string"), और सभी प्रक्रियाओं को फाइल सिस्टम के संदर्भ में विनिमय करते हैं। , हम ऑपरेटिंग सिस्टम की एक बहुत साफ तस्वीर प्राप्त कर सकते हैं।
eel ghEEz

@eelghEEz, यह वही दृष्टिकोण है जो डेटामेट डेटाबेस में ले जाता है।
जेसन

1
प्रतिमानों के बीच स्पष्ट और संक्षिप्त तुलना के लिए +1। एक सुझाव के int double(x){ return x * (++y); }बाद से वर्तमान एक अभी भी 4 होगा, हालांकि अभी भी एक अनजाने पक्ष प्रभाव है, जबकि ++yवापस आ जाएगी 6.
BrainFRZ

@eelghEEz मैं एक विकल्प के बारे में निश्चित नहीं हूं, वास्तव में, क्या कोई और है? एक (शुद्ध-) एफपी संदर्भ में जानकारी पेश करने के लिए, आप "एक माप लेते हैं", उदाहरण के लिए "टाइमस्टैम्प एक्स में, तापमान वाई" है। यदि कोई तापमान के बारे में पूछता है, तो उनका मतलब एक्स = अब हो सकता है, लेकिन वे संभवतः तापमान को समय के सार्वभौमिक कार्य के रूप में नहीं पूछ सकते हैं, है ना? एफपी अपरिवर्तनीय स्थिति से संबंधित है, और आपको एक अपरिवर्तनीय स्थिति - आंतरिक और बाहरी स्रोतों से - एक उत्परिवर्ती से बनाना है। इंडिसेस, टाइमस्टैम्प, आदि उपयोगी हैं लेकिन ऑर्थोगोनल टू म्यूटबिलिटी - जैसे वीसीएस संस्करण नियंत्रण के लिए हैं।
जॉन पी

29

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

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

मौजूदा प्रतिमान को इस प्रतिमान में अनुवादित करना 'कठिन' हो सकता है, लेकिन ऐसा इसलिए है क्योंकि इसमें वास्तव में कोड के बारे में पूरी तरह से अलग तरीके की आवश्यकता होती है। एक पक्ष-प्रभाव के रूप में, हालांकि अधिकांश मामलों में आपको समानता के लिए बहुत सारे अवसर मुफ्त में मिलते हैं।

परिशिष्ट: (अपने मूल्यों को ट्रैक करने के तरीके को बदलने के बारे में अपने संपादन के बारे में)
उन्हें निश्चित रूप से एक अपरिवर्तनीय डेटा संरचना में संग्रहीत किया जाएगा ...

यह एक सुझाया गया 'समाधान' नहीं है, बल्कि यह देखने का सबसे आसान तरीका है कि यह हमेशा काम करेगा कि आप इन अपरिवर्तनीय मूल्यों को एक 'चर नाम' के आधार पर संरचना जैसे मानचित्र (शब्दकोश / हैशटेबल) में संग्रहीत कर सकते हैं।

स्पष्ट रूप से व्यावहारिक समाधानों में आप एक अधिक समझदार दृष्टिकोण का उपयोग करेंगे, लेकिन यह सबसे खराब स्थिति दिखाता है कि अगर और कुछ भी काम नहीं करता तो आप इस तरह के नक्शे के साथ परस्पर स्थिति को 'अनुकरण' कर सकते हैं जिसे आप अपने आह्वान वृक्ष के माध्यम से ले जाते हैं।


2
ठीक है, मैंने शीर्षक बदल दिया। आपके उत्तर से लगता है कि समस्या और भी बदतर हो सकती है। अगर मुझे हर बार अपने राज्य में कुछ बदलने के लिए हर वस्तु को फिर से बनाना है, तो मैं अपना सारा सीपीयू समय कुछ नहीं बल्कि वस्तुओं के निर्माण में बिताऊंगा। मैं यहां गेम प्रोग्रामिंग के बारे में सोच रहा हूं, जहां आपको स्क्रीन पर (और ऑफ-स्क्रीन) एक साथ कई चीजें घूम रही हैं, जो एक-दूसरे के साथ बातचीत करने में सक्षम होने की आवश्यकता है। पूरे इंजन में एक सेट फ्रैमरेट है: सब कुछ जो आप करने जा रहे हैं, आपको एक्स नंबर मिलिशेकेंड में करना होगा। निश्चित रूप से लगातार पूरी वस्तुओं को रिसाइकल करने से बेहतर तरीका है?
मेसन व्हीलर

4
इसकी खूबी यह है कि कार्यान्वयन पर भाषा की नक़ल है, न कि कार्यान्वयन पर। कुछ तरकीबों के साथ, आप भाषा में imutable स्थिति रख सकते हैं, जबकि कार्यान्वयन वास्तव में राज्य को जगह में बदल रहा है। उदाहरण के लिए देखें हास्केल की एसटी मोनाड।
सीजरबी

4
@ मेसन: बिंदु यह है कि कंपाइलर यह बेहतर निर्णय ले सकता है कि वह राज्य (थ्रेड) सुरक्षित है जहां आप कर सकते हैं राज्य में जगह बदलने के लिए सुरक्षित है।
jerryjvl

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

@LegendLength आप खुद का विरोधाभास कर रहे हैं।
Ixx

18

मुझे लगता है कि थोड़ी सी गलतफहमी है। शुद्ध कार्यात्मक कार्यक्रमों में राज्य होता है। अंतर यह है कि उस स्थिति को कैसे मॉडल किया जाता है। शुद्ध कार्यात्मक प्रोग्रामिंग में, राज्य को उन कार्यों द्वारा हेरफेर किया जाता है जो कुछ राज्य लेते हैं और अगले राज्य को वापस करते हैं। राज्यों के माध्यम से अनुक्रमण तब शुद्ध कार्यों के अनुक्रम के माध्यम से राज्य को पारित करके प्राप्त किया जाता है।

यहां तक ​​कि वैश्विक उत्परिवर्ती राज्य को इस तरह से मॉडल किया जा सकता है। हास्केल में, उदाहरण के लिए, एक कार्यक्रम एक दुनिया से एक दुनिया के लिए एक समारोह है। यही है, आप पूरे ब्रह्मांड में गुजरते हैं , और कार्यक्रम एक नया ब्रह्मांड लौटाता है। व्यवहार में, हालांकि, आपको केवल ब्रह्मांड के उन हिस्सों में गुजरने की जरूरत है जिसमें आपका कार्यक्रम वास्तव में रुचि रखता है। और प्रोग्राम वास्तव में क्रियाओं का एक क्रम देते हैं जो ऑपरेटिंग वातावरण के निर्देश के रूप में कार्य करते हैं जिसमें कार्यक्रम चलता है।

आप इसे अनिवार्य प्रोग्रामिंग के संदर्भ में देखना चाहते थे। ठीक है, चलो एक कार्यात्मक भाषा में कुछ वास्तव में सरल अनिवार्य प्रोग्रामिंग को देखते हैं।

इस कोड पर विचार करें:

int x = 1;
int y = x + 1;
x = x + y;
return x;

सुंदर दलदल मानक अनिवार्य कोड। दिलचस्प कुछ भी नहीं करता है, लेकिन यह चित्रण के लिए ठीक है। मुझे लगता है कि आप सहमत होंगे कि यहां राज्य शामिल है। X चर का मान समय के साथ बदलता है। अब, नए सिंटैक्स का आविष्कार करके संकेतन को थोड़ा बदल दें:

let x = 1 in
let y = x + 1 in
let z = x + y in z 

इसे स्पष्ट करने के लिए कोष्ठक लगाएं, इसका क्या अर्थ है:

let x = 1 in (let y = x + 1 in (let z = x + y in (z)))

तो आप देखते हैं, राज्य शुद्ध अभिव्यक्तियों के अनुक्रम द्वारा तैयार किया गया है जो निम्नलिखित अभिव्यक्तियों के मुक्त चर को बांधता है।

आप पाएंगे कि यह पैटर्न किसी भी तरह के राज्य को मॉडल कर सकता है, यहां तक ​​कि आईओ भी।


क्या वह मोनाड जैसा है?
CMCDragonkai

क्या आप इस पर विचार करेंगे: ए 1 स्तर पर घोषणात्मक है स्तर 2 पर बी घोषणात्मक है, यह ए को अनिवार्य मानता है। सी 3 स्तर पर घोषणात्मक है, यह बी को अनिवार्य मानता है। जैसा कि हम अमूर्त परत को बढ़ाते हैं, यह हमेशा अमूर्त परत पर भाषाओं को कमतर मानती है और तब अधिक आवश्यक हो जाती है।
CMCDragonkai

14

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

f_imperative(y) {
  local x;
  x := e;
  while p(x, y) do
    x := g(x, y)
  return h(x, y)
}

यह कार्यात्मक कोड बन जाता है (स्कीम जैसा सिंटैक्स):

(define (f-functional y) 
  (letrec (
     (f-helper (lambda (x y)
                  (if (p x y) 
                     (f-helper (g x y) y)
                     (h x y)))))
     (f-helper e y)))

या यह हास्केलिश कोड

f_fun y = h x_final y
   where x_initial = e
         x_final   = loop x_initial
         loop x = if p x y then loop (g x y) else x

जैसे कि कार्यात्मक प्रोग्रामर ऐसा क्यों करना पसंद करते हैं (जो आपने नहीं पूछा), आपके प्रोग्राम के जितने अधिक टुकड़े स्टेटलेस हैं, उतने ही तरीके हैं कि बिना किसी ब्रेक के टुकड़ों को एक साथ रखा जाए । स्टेटलेस प्रतिमान की शक्ति प्रति व्यक्ति स्टेटलेसनेस (या शुद्धता) में नहीं है , लेकिन यह क्षमता आपको शक्तिशाली, पुन: प्रयोज्य कार्यों को लिखने और उन्हें संयोजित करने की क्षमता प्रदान करती है।

आप जॉन ह्यूजेस के पेपर व्हाई फंक्शनल प्रोग्रामिंग मैटर्स में बहुत सारे उदाहरणों के साथ एक अच्छा ट्यूटोरियल पा सकते हैं ।


13

यह एक ही काम करने के अलग-अलग तरीके हैं।

एक सरल उदाहरण पर विचार करें जैसे कि संख्या 3, 5 और 10 को जोड़ना। ऐसा करने के बारे में सोचें कि पहले 3 के मान को 5 में जोड़कर, फिर 10 को उस "3" में जोड़कर, उसके बाद के मान को आउटपुट करें। 3 ”(18)। यह धैर्यपूर्वक हास्यास्पद लगता है, लेकिन यह उस तरह से है जैसे कि राज्य आधारित अनिवार्य प्रोग्रामिंग अक्सर किया जाता है। वास्तव में, आपके पास कई अलग-अलग "3" हैं जिनका मूल्य 3 है, फिर भी अलग-अलग हैं। यह सब कुछ अजीब लगता है, क्योंकि हम इसके साथ बहुत उलझ चुके हैं, काफी समझदार, विचार यह है कि संख्या अपरिवर्तनीय है।

जब आप अपरिवर्तनीय मान लेते हैं, तो 3, 5 और 10 को जोड़ने के बारे में सोचें। आप 3 और 5 को एक और मान, 8 का उत्पादन करने के लिए जोड़ते हैं, फिर आप उस मूल्य में 10 जोड़कर एक और मूल्य, 18 का उत्पादन करते हैं।

ये समान कार्य करने के समान तरीके हैं। सभी आवश्यक जानकारी दोनों विधियों में मौजूद हैं, लेकिन विभिन्न रूपों में। एक जानकारी में राज्य के रूप में और बदलते राज्य के नियमों में मौजूद है। अन्य जानकारी में अपरिवर्तनीय डेटा और कार्यात्मक परिभाषाएं मौजूद हैं।


10

मुझे चर्चा में देर हो रही है, लेकिन मैं उन लोगों के लिए कुछ बिंदु जोड़ना चाहता था जो कार्यात्मक प्रोग्रामिंग से जूझ रहे हैं।

  1. कार्यात्मक भाषाएं समान राज्य अपडेट को अनिवार्य भाषाओं के रूप में बनाए रखती हैं, लेकिन वे अपडेटेड राज्य को बाद के फ़ंक्शन कॉल में पास करके करती हैं । यहाँ एक बहुत सरल उदाहरण है एक संख्या रेखा के नीचे यात्रा करना। आपका राज्य आपका वर्तमान स्थान है।

पहला अनिवार्य तरीका (छद्मकोश में)

moveTo(dest, cur):
    while (cur != dest):
         if (cur < dest):
             cur += 1
         else:
             cur -= 1
    return cur

अब कार्यात्मक तरीका (स्यूडोकोड में)। मैं टर्नरी ऑपरेटर पर बहुत अधिक झुकाव कर रहा हूं क्योंकि मैं चाहता हूं कि अनिवार्य पृष्ठभूमि के लोग वास्तव में इस कोड को पढ़ने में सक्षम हों। इसलिए यदि आप टर्नरी ऑपरेटर का अधिक उपयोग नहीं करते हैं (मैं हमेशा अपने अनिवार्य दिनों में इसे टालता हूं) यहां बताया गया है कि यह कैसे काम करता है।

predicate ? if-true-expression : if-false-expression

आप झूठी-अभिव्यक्ति के स्थान पर एक नया टर्नरी अभिव्यक्ति डालकर टर्नरी अभिव्यक्ति की श्रृंखला बना सकते हैं

predicate1 ? if-true1-expression :
predicate2 ? if-true2-expression :
else-expression

तो यह ध्यान में रखते हुए, यहाँ कार्यात्मक संस्करण है।

moveTo(dest, cur):
    return (
        cur == dest ? return cur :
        cur < dest ? moveTo(dest, cur + 1) : 
        moveTo(dest, cur - 1)
    )

यह एक तुच्छ उदाहरण है। यदि यह गेम की दुनिया में लोगों को घुमा रहा है, तो आपको स्क्रीन पर ऑब्जेक्ट की वर्तमान स्थिति को आकर्षित करने और ऑब्जेक्ट को कितनी तेजी से चलता है इसके आधार पर प्रत्येक कॉल में देरी का परिचय देने जैसे साइड इफेक्ट्स पेश करने होंगे। लेकिन आपको अभी भी परिवर्तनशील स्थिति की आवश्यकता नहीं होगी।

  1. सबक यह है कि कार्यात्मक भाषाएं विभिन्न मापदंडों के साथ फ़ंक्शन को कॉल करके "म्यूट" राज्य करती हैं। जाहिर है यह वास्तव में किसी भी चर को उत्परिवर्तित नहीं करता है, लेकिन यह है कि आप एक समान प्रभाव कैसे प्राप्त करते हैं। इसका मतलब है कि यदि आप कार्यात्मक प्रोग्रामिंग करना चाहते हैं तो आपको पुनरावर्ती सोचने की आदत डालनी होगी।

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

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


8

वास्तव में यह काफी आसान है कि कुछ ऐसा हो जो उत्परिवर्तित अवस्था के बिना भी भाषा में उत्परिवर्तनीय अवस्था जैसा दिखता हो।

प्रकार के साथ एक फ़ंक्शन पर विचार करें s -> (a, s)। हास्केल सिंटैक्स से अनुवाद, इसका अर्थ है एक फ़ंक्शन जो प्रकार का एक पैरामीटर " s" लेता है और " a" और " s" प्रकारों की एक जोड़ी देता है । यदि sहमारे राज्य का प्रकार है, तो यह फ़ंक्शन एक राज्य लेता है और एक नया राज्य लौटाता है, और संभवतः एक मान (आप हमेशा "यूनिट" उर्फ ​​वापस कर सकते हैं (), जो कि voidसी / सी ++ में " " के बराबर है , जैसा कि " a" प्रकार)। यदि आप इस प्रकार के कार्यों की कई कॉल को चेन करते हैं (राज्य को एक फ़ंक्शन से लौटाया जाता है और इसे अगले में पास किया जाता है), तो आपके पास "म्यूटेबल" स्थिति होती है (वास्तव में आप प्रत्येक फ़ंक्शन में एक नया राज्य बनाते हैं और पुराने को छोड़ देते हैं )।

यह समझने में आसान हो सकता है कि क्या आप परिवर्तनशील स्थिति को "अंतरिक्ष" के रूप में कल्पना करते हैं जहां आपका कार्यक्रम निष्पादित हो रहा है, और फिर समय आयाम के बारे में सोचें। तत्काल t1 पर, "स्पेस" एक निश्चित स्थिति में है (उदाहरण के लिए कुछ मेमोरी लोकेशन का मान 5 है)। बाद में तत्काल t2 में, यह एक अलग स्थिति में है (उदाहरण के लिए कि स्मृति स्थान का अब मूल्य 10 है)। इनमें से प्रत्येक समय "स्लाइस" एक राज्य है, और यह अपरिवर्तनीय है (आप उन्हें बदलने के लिए समय में वापस नहीं जा सकते)। तो, इस दृष्टिकोण से, आप एक समय तीर (आपकी परस्पर अवस्था) के साथ पूर्ण स्पेसटाइम से स्पेसटाइम (कई अपरिवर्तनीय राज्यों) के स्लाइस के सेट पर गए, और आपका प्रोग्राम प्रत्येक स्लाइस को एक मान के रूप में मान रहा है और प्रत्येक की गणना कर रहा है उनमें से एक समारोह के रूप में पिछले एक के लिए आवेदन किया।

ठीक है, शायद यह समझना आसान नहीं था :-)

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

एक उदाहरण के रूप में, मान लीजिए कि कंपाइलर हमें निम्नलिखित कार्य देता है:

readRef :: Ref a -> State# -> (a, State#)
writeRef :: Ref a -> a -> State# -> (a, State#)

इन हास्केल जैसी घोषणाओं से अनुवाद करने पर, readRefकुछ ऐसा मिलता है, जो एक पॉइंटर जैसा दिखता है या एक प्रकार का मान " a", और नकली राज्य, और aपहले पैरामीटर और एक नए नकली राज्य द्वारा इंगित प्रकार का मान लौटाता है । writeRefसमान है, लेकिन बदले गए मान को बदलता है।

यदि आप कॉल करते हैं readRefऔर फिर इसे नकली राज्य द्वारा पास करते हैं writeRef(शायद बीच में असंबद्ध कार्यों के लिए अन्य कॉल के साथ; ये राज्य मान फ़ंक्शन कॉल की "श्रृंखला" बनाते हैं), यह लिखित मान को वापस कर देगा। आप writeRefफिर से उसी पॉइंटर / हैंडल से कॉल कर सकते हैं और यह उसी मेमोरी लोकेशन पर लिखेगा - लेकिन, वैचारिक रूप से यह एक नया (नकली) राज्य लौटा रहा है, (नकली) स्थिति अभी भी imutable है (एक नया बनाया गया है) ")। कंपाइलर फंक्शन्स को उस क्रम में कॉल करेगा, जब उन्हें वास्तविक स्टेट वैरिएबल में कॉल करना होगा, जिसकी गणना की जानी थी, लेकिन एकमात्र राज्य जिसमें वास्तविक हार्डवेयर की पूर्ण (म्यूटेबल) स्थिति है।

(जो लोग हास्केल जानता हूँ कि मैं चीजों को एक बहुत सरल किया है और ommited कई महत्वपूर्ण जानकारी देखेंगे। जो लोग चाहते हैं अधिक विवरण देखने के लिए, पर एक नज़र Control.Monad.Stateसे mtl, और कम से ST sऔर IO(उर्फ ST RealWorld) monads।)

आप आश्चर्यचकित हो सकते हैं कि इसे इस तरह के गोल-मटोल तरीके से करने के बजाय (केवल भाषा में परस्पर स्थिति होने के कारण)। वास्तविक लाभ यह है कि आपने अपने कार्यक्रम की स्थिति को सुधार लिया है । पहले क्या निहित था (आपका कार्यक्रम राज्य वैश्विक था, थोड़ी दूरी पर कार्रवाई जैसी चीजों के लिए अनुमति ) अब स्पष्ट है। राज्य को प्राप्त और वापस नहीं आने वाले कार्य इसे संशोधित नहीं कर सकते हैं या इससे प्रभावित नहीं हो सकते हैं; वे "शुद्ध" हैं। इससे भी बेहतर, आपके पास अलग-अलग स्टेट थ्रेड हो सकते हैं, और थोड़े प्रकार के जादू के साथ, उन्हें शुद्ध एक के भीतर एक अनिवार्यता को एम्बेड करने के लिए इस्तेमाल किया जा सकता है, बिना इसे अशुद्ध किए बिना ( STहास्केल में मोनाड वह है जो आमतौर पर इस चाल के लिए उपयोग किया जाता है; State#मैं उपर्युक्त तथ्य GHC के दशक में है State# sकी इसके कार्यान्वयन के द्वारा प्रयोग किया, STऔरIO monads)।


7

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


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

5

दूसरों को दे रहे महान जवाबों के अलावा, कक्षाओं Integerऔर Stringजावा के बारे में सोचें । इन वर्गों के उदाहरण अपरिवर्तनीय हैं, लेकिन इससे कक्षाएं बेकार नहीं जाती हैं क्योंकि उनके उदाहरण नहीं बदले जा सकते हैं। अपरिवर्तनशीलता आपको कुछ सुरक्षा प्रदान करती है। आप जानते हैं कि यदि आप स्ट्रिंग या इंटेगर उदाहरण का उपयोग कुंजी के रूप में करते हैं Map, तो कुंजी को बदला नहीं जा सकता। Dateजावा में वर्ग से इसकी तुलना करें :

Date date = new Date();
mymap.put(date, date.toString());
// Some time later:
date.setTime(new Date().getTime());

आपने चुपचाप अपने नक्शे में एक कुंजी बदल दी है! अपरिवर्तनीय वस्तुओं के साथ काम करना, जैसे कि कार्यात्मक प्रोग्रामिंग में, बहुत क्लीनर है। यह साइड इफेक्ट होने के कारण के लिए आसान है - कोई नहीं! इसका मतलब है कि यह प्रोग्रामर के लिए आसान है, और ऑप्टिमाइज़र के लिए भी आसान है।


2
मैं समझता हूँ कि, लेकिन यह मेरे सवाल का जवाब नहीं है। यह ध्यान में रखते हुए कि कंप्यूटर प्रोग्राम किसी वास्तविक दुनिया की घटना या प्रक्रिया का एक मॉडल है, यदि आप अपने मूल्यों को नहीं बदल सकते हैं, तो आप कुछ ऐसा कैसे बदलते हैं?
मेसन व्हीलर

ठीक है, आप निश्चित रूप से इंटेगर और स्ट्रिंग कक्षाओं के साथ उपयोगी चीजें कर सकते हैं। ऐसा नहीं है कि उनकी अपरिवर्तनीयता का मतलब है कि आपके पास परिवर्तनशील स्थिति नहीं हो सकती है।
एडी

@ मेसन व्हीलर - यह समझकर कि एक चीज और यह दो अलग-अलग "चीजें" हैं। क्या pacman समय-समय पर बी से नहीं बदलता है। जहाँ pacman में बदलाव होता है। जब आप समय-समय पर बी से आगे बढ़ते हैं, तो आपको pacman + राज्य का एक नया संयोजन मिलता है ... जो कि एक ही pacman है, अलग राज्य। नहीं बदला राज्य ... अलग राज्य।
RHSeeger

4

खेल जैसे अत्यधिक इंटरैक्टिव अनुप्रयोगों के लिए, कार्यात्मक प्रतिक्रियाशील प्रोग्रामिंग आपका मित्र है: यदि आप अपने खेल की दुनिया के गुणों को समय-भिन्न मूल्यों (और / या घटना धाराओं) के रूप में तैयार कर सकते हैं, तो आप तैयार हैं! ये सूत्र कभी-कभी एक राज्य को बदलने की तुलना में और भी अधिक प्राकृतिक और अभिप्राय प्रकट करते हैं, उदाहरण के लिए एक चलती गेंद के लिए, आप सीधे प्रसिद्ध कानून x = v * t का उपयोग कर सकते हैं । और क्या बेहतर है, खेल के नियमों लिखा इस तरह से लिखें की तुलना में वस्तु उन्मुख कपोल-कल्पना बेहतर। उदाहरण के लिए, इस मामले में, गेंद की गति भी समय-भिन्न मूल्य हो सकती है, जो गेंद की टक्करों से मिलकर घटना की धारा पर निर्भर करती है। अधिक ठोस डिजाइन विचारों के लिए, एल्म में मेकिंग गेम्स देखें ।


4

3

इसी तरह से FORTRAN, COMMON ब्लॉकों के बिना काम करेगा: आप उन तरीकों को लिखेंगे जिनमें आपके पास और स्थानीय चर में दिए गए मान थे। बस।

ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग ने हमें राज्य और व्यवहार को एक साथ लाया, लेकिन यह एक नया विचार था जब मैंने पहली बार 1994 में C ++ से इसका सामना किया।

Geez, मैं एक कार्यात्मक प्रोग्रामर था जब मैं एक मैकेनिकल इंजीनियर था और मुझे यह नहीं पता था!


2
मैं असहमत हूँ कि यह कुछ ऐसा है जिसे आप OO पर पिन कर सकते हैं। ऊ से पहले की भाषाओं ने युग्मन राज्य और एल्गोरिदम को प्रोत्साहित किया। OO ने इसे प्रबंधित करने के लिए एक बेहतर तरीका प्रदान किया।
जेसन बेकर

"प्रोत्साहित" - शायद। OO इसे भाषा का एक स्पष्ट हिस्सा बनाते हैं। आप C में छुपा और जानकारी छुपा सकते हैं, लेकिन मैं कहूंगा कि OO भाषाएँ इसे बहुत आसान बनाती हैं।
duffymo

2

ध्यान में रखें: कार्यात्मक भाषाएं पूर्ण होती हैं। इसलिए, किसी भी उपयोगी कार्य को आप एक अनिवार्य भाषा में करेंगे जो एक कार्यात्मक भाषा में किया जा सकता है। हालांकि दिन के अंत में, मुझे लगता है कि हाइब्रिड दृष्टिकोण के बारे में कुछ कहा जाना चाहिए। F # और Clojure (और मुझे यकीन है कि अन्य) जैसी भाषाएं स्टेटलेस डिज़ाइन को प्रोत्साहित करती हैं, लेकिन जब आवश्यक हो तो परिवर्तनशीलता की अनुमति देती हैं।


सिर्फ इसलिए कि दो भाषाएँ पूरी हो रही हैं इसका मतलब यह नहीं है कि वे समान कार्य कर सकते हैं। इसका मतलब यह है कि वे एक ही गणना कर सकते हैं। ब्रेनफक ट्यूरिंग पूर्ण है, लेकिन मैं काफी निश्चित हूं कि यह टीसीपी स्टैक पर संवाद नहीं कर सकता है।
RHSeeger

2
ज़रूर कर सकते हैं। सी के रूप में हार्डवेयर के लिए समान पहुंच को देखते हुए, यह कर सकता है। इसका मतलब यह नहीं है कि यह व्यावहारिक होगा, लेकिन संभावना है।
जेसन बेकर

2

आपके पास एक शुद्ध कार्यात्मक भाषा नहीं हो सकती जो उपयोगी हो। हमेशा एक परिवर्तनशीलता का स्तर होगा जिसे आपको निपटना होगा, आईओ एक उदाहरण है।

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


1

बहुत सारी पुनरावृत्ति का उपयोग करके।

एफ # में टिक टीएसी को पैर की अंगुली (एक कार्यात्मक भाषा।)


बेशक, पूंछ-पुनरावृत्ति को बहुत कुशलता से लागू किया जा सकता है, क्योंकि संकलक इसे एक लूप में बदल सकते हैं।
एडी

-3

यह बहुत सरल है। आप कार्यात्मक प्रोग्रामिंग में जितने चाहें उतने चर का उपयोग कर सकते हैं ... लेकिन केवल तभी जब वे स्थानीय चर (कार्यों के अंदर निहित) हों। तो बस अपने कोड को फ़ंक्शंस में लपेटें, मानों को उन फ़ंक्शंस के बीच आगे-पीछे करें (जैसे पास किए गए मानदंड और वापस दिए गए मान) ... और यही सब कुछ है!

यहाँ एक उदाहरण है:

function ReadDataFromKeyboard() {
    $input_values = $_POST[];
    return $input_values;
}
function ProcessInformation($input_values) {
    if ($input_values['a'] > 10)
        return ($input_values['a'] + $input_values['b'] + 3);
    else if ($input_values['a'] > 5)
        return ($input_values['b'] * 3);
    else
        return ($input_values['b'] - $input_values['a'] - 7);
}
function DisplayToPage($data) {
    print "Based your input, the answer is: ";
    print $data;
    print "\n";
}

/* begin: */
DisplayToPage (
    ProcessInformation (
        GetDataFromKeyboard()
    )
);

जॉन, यह कौन सी भाषा है?
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.