कैसे एक कार्यात्मक में एक OO कार्यक्रम को प्रतिबिंबित करने के लिए?


26

मुझे कार्यात्मक शैली में प्रोग्राम लिखने के तरीके पर संसाधन खोजने में कठिनाई हो रही है। सबसे उन्नत विषय जिसे मैं ऑनलाइन चर्चा में पा सकता था, वह श्रेणीबद्ध पदानुक्रमों में कटौती करने के लिए संरचनात्मक टाइपिंग का उपयोग कर रहा था; सबसे अधिक बस कैसे लूप को बदलने के लिए नक्शा / गुना / कम / आदि का उपयोग करने के लिए सौदा।

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


6
मुझे नहीं लगता कि यह संभव है। आपको फिर से सब कुछ नया (और फिर से लिखना) करना होगा।
ब्रायन चेन

18
-1, यह पद इस गलत धारणा से पक्षपाती है कि OOP और कार्यात्मक शैली इसके विपरीत हैं। वे ज्यादातर ऑर्थोगोनल अवधारणाएं हैं, और IMHO एक मिथक है कि वे नहीं हैं। "कार्यात्मक" "प्रक्रियात्मक" के लिए अधिक विरोध किया जाता है, और दोनों शैलियों का उपयोग ओओपी के साथ संयोजन में किया जा सकता है।
डॉक्टर ब्राउन

11
@DocBrown, OOP एक परिवर्तनशील स्थिति पर बहुत अधिक निर्भर करता है। स्टेटलेस ऑब्जेक्ट्स वर्तमान ओओपी डिजाइन अभ्यास में अच्छी तरह से फिट नहीं होते हैं।
एसके-तर्क

9
@ एसके-तर्क: कुंजी स्टेटलेस ऑब्जेक्ट्स नहीं हैं, लेकिन अपरिवर्तनीय ऑब्जेक्ट हैं। और यहां तक ​​कि जब ऑब्जेक्ट परस्पर भिन्न होते हैं, तो वे अक्सर सिस्टम के एक कार्यात्मक हिस्से में उपयोग किए जा सकते हैं जब तक कि वे दिए गए संदर्भ में बदल नहीं जाते हैं। इसके अलावा, मुझे लगता है कि आप वस्तुओं को जानते हैं और बंद कर देने योग्य हैं। तो यह सब दिखाता है कि ओओपी और "कार्यात्मक" विपरीत नहीं हैं।
डॉक्टर ब्राउन

12
@DocBrown: मुझे लगता है कि भाषा निर्माण ऑर्थोगोनल हैं, जबकि मानसिकता टकराती हैं। OOP के लोग यह पूछते हैं कि "वस्तुएं क्या हैं और वे कैसे सहयोग करती हैं?"; कार्यात्मक लोग पूछते हैं कि "मेरा डेटा क्या है, और मैं इसे कैसे बदलना चाहता हूं?"। वे एक जैसे सवाल नहीं हैं, और वे अलग-अलग जवाब देते हैं। मुझे भी लगता है कि आपने प्रश्न को गलत बताया। यह "ओओपी ड्रॉल्स और एफपी नियम नहीं है, मैं ओओपी से कैसे छुटकारा पाऊं?", यह "मुझे ओओपी मिलता है और मुझे एफपी नहीं मिलता है, क्या ओओपी प्रोग्राम को एक कार्यात्मक में बदलने का एक तरीका है, इसलिए मुझे मिल सकता है" कुछ अंतर्दृष्टि? ”।
माइकल शॉ

जवाबों:


31

कार्यात्मक प्रोग्रामिंग की परिभाषा

जॉय ऑफ क्लोजर का परिचय निम्नलिखित कहता है:

फ़ंक्शनल प्रोग्रामिंग उन कंप्यूटिंग टर्म्स में से एक है जिसमें एक अनाकार परिभाषा है। यदि आप 100 प्रोग्रामर से उनकी परिभाषा पूछते हैं, तो आपको 100 अलग-अलग उत्तर प्राप्त होंगे ...

कार्यात्मक प्रोग्रामिंग चिंताएं और कार्यों के अनुप्रयोग और संरचना की सुविधा देती हैं ... किसी भाषा को कार्यात्मक माना जाने के लिए, इसके कार्य की धारणा प्रथम श्रेणी में होनी चाहिए। प्रथम श्रेणी के कार्यों को डेटा के किसी भी अन्य टुकड़े की तरह ही संग्रहीत, पास और लौटाया जा सकता है। इस मूल अवधारणा से परे, [एफपी की परिभाषाएँ शामिल हो सकती हैं] शुद्धता, अपरिवर्तनीयता, पुनरावृत्ति, आलस्य और संदर्भात्मक पारदर्शिता।

स्केल 2 एडिशन पी में प्रोग्रामिंग । 10 की निम्नलिखित परिभाषा है:

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

फ़ंक्शनल प्रोग्रामिंग का दूसरा मुख्य विचार यह है कि किसी प्रोग्राम के संचालन को इनपुट वैल्यूज़ को आउटपुट वैल्यू में बदलने के बजाय डेटा में बदलाव करना चाहिए।

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

प्रथम श्रेणी के कार्य

आप वर्तमान में अपने बस ऑब्जेक्ट से यात्रियों की एक सूची प्राप्त करते हैं और आप बस किराया की राशि से प्रत्येक यात्री के बैंक खाते को कम करने पर इसे पुन: व्यवस्थित करते हैं। इसी क्रिया को करने का कार्यात्मक तरीका बस में एक विधि होना होगा, शायद जिसे फॉरचैन्जर कहा जाता है जो एक तर्क का कार्य करता है। फिर बस अपने यात्रियों के ऊपर से पुनरावृत्ति करेगा, लेकिन यह सबसे अच्छा है और आपका ग्राहक कोड जो सवारी के लिए किराया वसूल करता है, उसे एक समारोह में रखा जाएगा और forEachPassenger को पास किया जाएगा। देखा! आप कार्यात्मक प्रोग्रामिंग का उपयोग कर रहे हैं।

अनिवार्य:

for (Passenger p : Bus.getPassengers()) {
    p.debit(fare);
}

फ़ंक्शनल (एक गुमनाम फ़ंक्शन या स्काला में "लैम्ब्डा" का उपयोग करके):

myBus = myBus.forEachPassenger(p:Passenger -> { p.debit(fare) })

अधिक मीठा स्काला संस्करण:

myBus = myBus.forEachPassenger(_.debit(fare))

गैर-प्रथम श्रेणी के कार्य

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

// Java 8 has java.util.function.Consumer, but in earlier
// versions you have to roll your own:
public interface Consumer<T> {
    public void accept(T t);
}

तब बस वर्ग एक आंतरिक पुनरावृत्ति प्रदान करता है:

public void forEachPassenger(Consumer<Passenger> c) {
    for (Passenger p : passengers) {
        c.accept(p);
    }
}

अंत में, आप बस के लिए एक अनाम फ़ंक्शन ऑब्जेक्ट पास करते हैं:

// Java 8 has syntactic sugar to make this look more like
// the Scala solution, but earlier versions require manually
// instantiating a "Function Object," in this case, a
// Consumer:
Bus.forEachPassenger(new Consumer<Passenger>() {
    @Override
    public void accept(final Passenger p) {
        p.debit(fare);
    }
}

जावा 8 स्थानीय चर को एक अनाम फ़ंक्शन के दायरे पर कब्जा करने की अनुमति देता है, लेकिन पहले के संस्करणों में, ऐसे किसी भी चर को अंतिम घोषित किया जाना चाहिए। इसके आस-पास जाने के लिए आपको MutableReference आवरण कक्षा बनाने की आवश्यकता हो सकती है। यहाँ एक पूर्णांक-विशिष्ट वर्ग है जो आपको उपरोक्त कोड में एक लूप काउंटर जोड़ने देता है:

public static class MutableIntWrapper {
    private int i;
    private MutableIntWrapper(int in) { i = in; }
    public static MutableIntWrapper ofZero() {
        return new MutableIntWrapper(0);
    }
    public int value() { return i; }
    public void increment() { i++; }
}

final MutableIntWrapper count = MutableIntWrapper.ofZero();
Bus.forEachPassenger(new Consumer<Passenger>() {
    @Override
    public void accept(final Passenger p) {
        p.debit(fare);
        count.increment();
    }
}

System.out.println(count.value());

इस कुरूपता के साथ, कभी-कभी आंतरिक पुनरावृत्ति प्रदान करके आपके पूरे कार्यक्रम में फैले लूप से जटिल और दोहराया तर्क को समाप्त करना फायदेमंद होता है।

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

अचल स्थिति

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

पवित्रता

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

यह कहा गया है कि कार्यात्मक प्रोग्रामिंग में, "काम करने के लिए आमतौर पर कुछ बुराई की आवश्यकता होती है।" 100% शुद्धता आमतौर पर लक्ष्य नहीं है। कम से कम दुष्प्रभाव है।

निष्कर्ष

वास्तव में, उपरोक्त सभी विचारों में, मेरे कोड को सरल बनाने के लिए व्यावहारिक अनुप्रयोगों के संदर्भ में अपरिवर्तनीयता सबसे बड़ी एकल जीत है - चाहे ओओपी, या एफपी। पुनरावृत्तियों के लिए कार्य करना दूसरी सबसे बड़ी जीत है। जावा 8 lambdas प्रलेखन क्यों का सबसे अच्छा व्याख्या है। पेड़ों की प्रोसेसिंग के लिए रिकर्सन शानदार है। आलस्य आपको अनंत संग्रहों के साथ काम करने की अनुमति देता है।

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

पुनश्च

जिमी हॉफ ने बताया कि मेरी बस की क्लास म्यूट है। मूल को ठीक करने के बजाय, मुझे लगता है कि यह वास्तव में इस प्रश्न के बारे में जिस तरह का प्रतिबिंबन है, वह प्रदर्शित करेगा। यह एक नई बस का उत्पादन करने के लिए एक कारखाने बस में प्रत्येक विधि बनाकर तय किया जा सकता है, प्रत्येक विधि यात्री पर एक नया यात्री उत्पादन करने के लिए एक कारखाना है। इस प्रकार मैंने हर चीज में एक रिटर्न टाइप जोड़ा है जिसका अर्थ है कि मैं Java 8 के java.util.function की नकल करूंगा। उपभोक्ता इंटरफ़ेस के बजाय।

public interface Function<T,R> {
    public R apply(T t);
    // Note: I'm leaving out Java 8's compose() method here for simplicity
}

फिर बस में:

public Bus mapPassengers(Function<Passenger,Passenger> c) {
    // I have to use a mutable collection internally because Java
    // does not have immutable collections that return modified copies
    // of themselves the way the Clojure and Scala collections do.
    List<Passenger> newPassengers = new ArrayList(passengers.size());
    for (Passenger p : passengers) {
        newPassengers.add(c.apply(p));
    }
    return Bus.of(driver, Collections.unmodifiableList(passengers));
}

अंत में, अनाम फ़ंक्शन ऑब्जेक्ट चीजों की संशोधित स्थिति (नए यात्रियों के साथ एक नई बस) लौटाता है। यह मानता है कि p.debit () अब मूल की तुलना में कम पैसे के साथ एक नया अपरिवर्तनीय यात्री देता है:

Bus b = b.mapPassengers(new Function<Passenger,Passenger>() {
    @Override
    public Passenger apply(final Passenger p) {
        return p.debit(fare);
    }
}

उम्मीद है कि अब आप अपना निर्णय ले सकते हैं कि आप अपनी अनिवार्य भाषा को कितना कार्यात्मक बनाना चाहते हैं, और यह तय करें कि एक कार्यात्मक भाषा का उपयोग करके अपनी परियोजना को नया स्वरूप देना बेहतर होगा। स्काला या क्लोजर में, संग्रह और अन्य एपीआई को कार्यात्मक प्रोग्रामिंग को आसान बनाने के लिए डिज़ाइन किया गया है। दोनों में बहुत अच्छा जावा इंटरॉप है, जिससे आप भाषाओं का मिश्रण और मिलान कर सकते हैं। वास्तव में, जावा इंटरऑपरेबिलिटी के लिए, स्काला अपने प्रथम श्रेणी के कार्यों को उन अनाम कक्षाओं में संकलित करता है जो जावा 8 कार्यात्मक इंटरफेस के साथ लगभग संगत हैं। आप स्काला में गहराई से संप्रदाय में विवरण के बारे में पढ़ सकते हैं १.३.२


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

और जैसे ही कोई फ़ंक्शन मान वापस नहीं करता है, अचानक फ़ंक्शन को दूसरों के साथ नहीं बनाया जा सकता है, और आप कार्यात्मक संरचना के सभी अमूर्त को खो देते हैं। आप फ़ंक्शन को ऑब्जेक्ट को जगह में बदल सकते हैं और फिर ऑब्जेक्ट को वापस कर सकते हैं, लेकिन यदि यह ऐसा कर रहा है तो क्यों न फ़ंक्शन को ऑब्जेक्ट को एक पैरामीटर के रूप में ले लें और इसे मूल ऑब्जेक्ट की सीमा से मुक्त कर दें? मूल वस्तु से मुक्त होने पर यह अन्य प्रकारों पर भी काम करने में सक्षम होगा , जो कि आपके द्वारा गायब किए गए FP का एक और महत्वपूर्ण हिस्सा है: प्रकार अमूर्त। आपका forEachPasenger केवल यात्रियों के खिलाफ काम करता है ...
जिमी

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

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

1
व्हाइटबोर्ड पर यह बातचीत जारी है: chat.stackexchange.com/transcript/message/11702383#11702383
GlenPeterson

12

मुझे इसका व्यक्तिगत अनुभव "पूरा" करना है। अंत में, मैं कुछ ऐसी चीज़ों के साथ नहीं आया जो विशुद्ध रूप से कार्यात्मक है, लेकिन मैं कुछ ऐसी चीज़ लेकर आया हूँ जिससे मैं खुश हूँ। यहाँ मैंने यह कैसे किया:

  • फ़ंक्शन के एक पैरामीटर को सभी बाहरी स्थिति में कनवर्ट करें। ईजी: यदि किसी ऑब्जेक्ट की विधि संशोधित होती है x, तो इसे बनाएं ताकि xकॉलिंग के बजाय विधि पास हो जाए this.x
  • वस्तुओं से व्यवहार हटा दें।
    1. वस्तु का डेटा सार्वजनिक रूप से सुलभ हो
    2. ऑब्जेक्ट को कॉल करने वाले कार्यों में सभी विधियों को कनवर्ट करें।
    3. क्लाइंट कोड है जो ऑब्जेक्ट को कॉल करता है ऑब्जेक्ट ऑब्जेक्ट में नए फ़ंक्शन को कॉल करता है। ईजी: Convert x.methodThatModifiesTheFooVar()मेंfooFn(x.foo)
    4. ऑब्जेक्ट से मूल विधि निकालें
  • उच्च आदेश कार्यों की तरह साथ के रूप में आप कर सकते हैं कई पुनरावृत्ति छोरों के रूप में बदल दें map, reduce, filter, आदि

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

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

संपादित करें

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

लेकिन अगर आप जावा का उपयोग कर रहे हैं, तो आप इन तकनीकों का उपयोग करके अपनी कक्षाओं को अपरिवर्तनीय बना सकते हैं।


+1 केवल इस बात पर निर्भर करता है कि आप वास्तव में क्या करने की कोशिश कर रहे हैं, यह संभवतः उतना ही है जितना कि आप वास्तव में बिना डिजाइन परिवर्तन किए जा सकते हैं जो कि केवल "रिफैक्टरिंग" से परे वाया हो जाएगा।
एवीकाटोस

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

मुझे लगता है कि उत्परिवर्तनीय स्थिति से छुटकारा पाना योग्यता होगा। मुझे नहीं लगता कि यह सिर्फ भाषा में बेहतर समर्थन का सवाल है, मुझे लगता है कि अपरिवर्तनीय से अपरिवर्तनीय तक जाने के लिए मूल रूप से हमेशा मूलभूत वास्तुकला परिवर्तनों की आवश्यकता होगी जो अनिवार्य रूप से एक पुनर्लेखन का गठन करते हैं। Ymmv हालांकि refactoring की आपकी परिभाषा पर निर्भर करता है।
एवीकाटोस

@ इवीकाटोस मेरा संपादन देखें
डैनियल कपलान

1
@tieTYT हाँ, यह दुःखद है कि JS इतना उत्परिवर्तित हो रहा है, लेकिन कम से कम
क्लोजर

3

मुझे नहीं लगता कि कार्यक्रम को पूरी तरह से फिर से तैयार करना संभव है - आपको सही प्रतिमान को फिर से डिज़ाइन और पुन: लागू करना होगा।

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

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


मैं यह जोड़ना चाहूंगा कि एक अच्छा पहला चिह्न है, संदर्भात्मक पारदर्शिता के लिए प्रयास करना। एक बार जब आप यह कर लेते हैं तो आपको कार्यात्मक प्रोग्रामिंग के लाभ का ~ 50% मिलता है।
डैनियल ग्रैज़र

3

मुझे लगता है कि लेखों की यह श्रृंखला ठीक वही है जो आप चाहते हैं:

विशुद्ध रूप से कार्यात्मक रेट्रो

http://prog21.dadgum.com/23.html भाग 1

http://prog21.dadgum.com/24.html भाग 2

http://prog21.dadgum.com/25.html भाग 3

http://prog21.dadgum.com/26.html भाग 4

http://prog21.dadgum.com/37.html अनुवर्ती

सारांश है:

लेखक साइड इफेक्ट्स के साथ एक मुख्य लूप का सुझाव देता है (साइड इफेक्ट कहीं न कहीं, सही होना चाहिए?) और अधिकांश फ़ंक्शन छोटे अपरिवर्तनीय रिकॉर्ड का विवरण देते हैं कि कैसे उन्होंने खेल की स्थिति को बदल दिया।

बेशक, जब आप एक वास्तविक दुनिया के कार्यक्रम को लिख रहे हैं, तो आप कई प्रोग्रामिंग शैलियों को मिलाएंगे और मेल करेंगे, हर एक का उपयोग करके जहां यह सबसे अधिक मदद करता है। हालाँकि, यह सबसे अच्छा कार्यात्मक / अपरिवर्तनीय तरीके से एक प्रोग्राम लिखने की कोशिश करने और इसे सबसे स्पेगेटी तरीके से लिखने का एक अच्छा सीखने का अनुभव है, केवल वैश्विक चर का उपयोग करते हुए :-) (इसे प्रयोग के रूप में करें, उत्पादन में नहीं, कृपया)


2

संभवतः आपको अपने सभी कोड को अंदर से चालू करना होगा क्योंकि OOP और FP के पास कोड को व्यवस्थित करने के लिए दो विपरीत दृष्टिकोण हैं।

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

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

नीचे पंक्ति: OOP और FP मूल रूप से कोड को व्यवस्थित करने के तरीके में भिन्न होते हैं, और OOP डिज़ाइन को FP डिज़ाइन में बदलने से यह प्रतिबिंबित करने के लिए आपके सभी कोड को बदलना होगा। यह एक दिलचस्प व्यायाम हो सकता है, हालांकि। इन लेक्चर नोट्स को भी मिक्मे द्वारा उद्धृत SICP बुक में देखें, विशेष रूप से 13.1.10 के माध्यम से 13.1.5।

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