क्या ऐसे फ़ंक्शंस जो पैरामीटर के रूप में फ़ंक्शंस लेते हैं, उन फ़ंक्शंस को भी मापदंडों के रूप में लेना चाहिए?


20

मैं अक्सर अपने आप को लिखने के कार्य करता हूं जो इस तरह दिखता है क्योंकि वे मुझे आसानी से डेटा एक्सेस का मखौल उड़ाने की अनुमति देते हैं, और फिर भी एक हस्ताक्षर प्रदान करते हैं जो मापदंडों को स्वीकार करता है कि क्या डेटा का उपयोग करना है।

public static string GetFormattedRate(
        Func<string, RateType>> getRate,
        string rateKey)
{
    var rate = getRate(rateKey);
    var formattedRate = rate.DollarsPerMonth.ToString("C0");
    return formattedRate;
}

या

public static string GetFormattedRate(
        Func<RateType, string> formatRate,
        Func<string, RateType>> getRate,
        string rateKey)
{
    var rate = getRate(rateKey);
    var formattedRate = formatRate(rate);
    return formattedRate;
}

तो मैं इसे कुछ इस तरह से उपयोग करता हूं:

using FormatterModule;

public static Main()
{
    var getRate = GetRateFunc(connectionStr);
    var formattedRate = GetFormattedRate(getRate, rateType);
    // or alternatively
    var formattedRate = GetFormattedRate(getRate, FormatterModule.FormatRate, rateKey);

    System.PrintLn(formattedRate);
}

क्या यह एक आम बात है? मुझे ऐसा लगता है कि मुझे कुछ और करना चाहिए

public static string GetFormattedRate(
        Func<RateType> getRate())
{
    var rate = getRate();
    return rate.DollarsPerMonth.ToString("C0");
}

लेकिन यह बहुत अच्छी तरह से काम नहीं करता है क्योंकि मुझे प्रत्येक दर प्रकार के लिए विधि में एक नया कार्य करना होगा।

कभी-कभी मुझे लगता है कि मुझे ऐसा करना चाहिए

public static string GetFormattedRate(RateType rate)
{
   return rate.DollarsPerMonth.ToString("C0");
}

लेकिन ऐसा लगता है कि किसी भी प्रकार के भ्रूण को हटाने और पुन: प्रयोज्य को प्रारूपित करने के लिए। जब भी मुझे लाने और फॉर्मेट करना होता है तो मुझे दो लाइनें लिखनी होती हैं, एक लाने के लिए और एक फॉर्मेट करने के लिए।

मैं कार्यात्मक प्रोग्रामिंग के बारे में क्या याद कर रहा हूं? क्या यह करने का सही तरीका है, या एक बेहतर पैटर्न है जिसे बनाए रखना और उपयोग करना दोनों आसान है?


50
DI कैंसर अब तक फैल चुका है ...
इदं आर्य

16
मैं यह देखने के लिए संघर्ष करता हूं कि इस संरचना का उपयोग पहली जगह में क्यों किया जाएगा। निश्चित रूप से यह एक पैरामीटर के रूप में रेट को स्वीकार करने के लिए अधिक सुविधाजनक (और स्पष्ट ) है GetFormattedRate(), क्योंकि यह एक फ़ंक्शन को स्वीकार करने का विरोध करता है जो रेट को एक पैरामीटर के रूप में प्रारूपित करता है?
एरोथ

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

7
@ इदानरी डीआई कैंसर?
जूल्स

11
@ जूल्स डिपेंडेंसी इंजेक्शन कैंसर
बिल्ली

जवाबों:


39

यदि आप यह बहुत लंबे समय तक करते हैं, तो आप अंततः खुद को इस फ़ंक्शन को बार-बार लिखेंगे:

public static Type3 CombineFunc1AndFunc2(
    Func<Type1, Type2> func1,
    Func<Type2, Type3>> func2,
    Type1 input)
{
    return func2(func1(input))
}

बधाई हो, आपने फ़ंक्शन रचना का आविष्कार किया है

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

public static Func<A, C> Compose(
    Func<B, C> outer, Func<A, B>> inner)
{
    return (input) => outer(inner(input))
}

var GetFormattedRate = Compose(FormatRate, GetRate);
var formattedRate = GetFormattedRate(rateKey);

जैसा कि यह खड़ा है, जो आप कर रहे हैं उसका उद्देश्य कम है। यह सामान्य नहीं है, इसलिए आपको पूरे स्थान पर उस कोड को डुप्लिकेट करना होगा। यह आपके कोड को ओवरकम्प्लीकेट करता है क्योंकि अब आपके कोड को एक हजार छोटे कार्यों से अपनी जरूरत की सभी चीजों को इकट्ठा करना है। हालांकि आपका दिल सही जगह पर है: आपको बस चीजों को एक साथ रखने के लिए इन प्रकार के सामान्य उच्च आदेश कार्यों का उपयोग करने की आदत डालने की आवश्यकता है। या, बारी Func<A, B>और Aमें एक अच्छे पुराने फैशन लैंबडा का उपयोग करें Func<B>

अपने आप को दोहराएं नहीं।


16
अपने आप को दोहराएं अगर खुद को दोहराने से बचने से कोड खराब हो जाता है। जैसे कि आप हमेशा उन दो लाइनों के बजाय लिखते हैं FormatRate(GetRate(rateKey))
user253751

6
@ मुझे पता है कि मुझे लगता है कि वह GetFormattedRateअब से सीधे उपयोग करने में सक्षम होगा ।
कार्ल्स

मुझे लगता है कि यह वही है जो मैं यहां करने की कोशिश कर रहा हूं, और मैंने पहले भी इस कंपोज फंक्शन की कोशिश की है, लेकिन ऐसा लगता है कि मुझे इसका उपयोग करने के लिए शायद ही कभी मिलता है क्योंकि मेरे दूसरे फ़ंक्शन को अक्सर एक से अधिक पैरामीटर की आवश्यकता होती है। शायद मुझे @ thomas-junk द्वारा उल्लिखित कॉन्फ़िगर फ़ंक्शंस के लिए क्लोजर के साथ संयोजन में ऐसा करने की आवश्यकता है
रशिंग

@rushinge इस प्रकार की रचना ठेठ FP फ़ंक्शन पर काम करती है, जिसमें हमेशा एक ही तर्क होता है (अतिरिक्त तर्क वास्तव में अपने स्वयं के कार्य हैं, जैसे सोचते हैं Func<Func<A, B>, C>); इसका मतलब है कि आपको केवल एक कंपोज़ फ़ंक्शन की आवश्यकता है जो किसी भी फ़ंक्शन के लिए काम करता है। हालाँकि, आप सी # फ़ंक्शन के साथ काम कर सकते हैं बस क्लोजर का उपयोग करते हुए - पास होने के बजाय Func<rateKey, rateType>, आपको वास्तव में ज़रूरत है Func<rateType>, और फ़ंक को पास करते समय, आप इसे पसंद करते हैं () => GetRate(rateKey)। मुद्दा यह है कि आप उन तर्कों को उजागर नहीं करते जिनके बारे में लक्ष्य फ़ंक्शन परवाह नहीं करता है।
लुआं

1
@immibis हाँ, Composeफ़ंक्शन वास्तव में केवल तभी उपयोगी है जब आपको GetRateकिसी कारण से निष्पादन में देरी करने की आवश्यकता होती है , जैसे कि यदि आप Compose(FormatRate, GetRate)किसी फ़ंक्शन को पास करना चाहते हैं जो अपने स्वयं के चुनने की दर प्रदान करता है, जैसे कि इसे प्रत्येक तत्व में लागू करना सूची।
jpaugh

107

किसी फ़ंक्शन और उसके मापदंडों को पारित करने का कोई कारण नहीं है, केवल तब उन मापदंडों के साथ इसे कॉल करें। वास्तव में, आपके मामले में आप एक समारोह पारित करने के लिए कोई कारण नहीं है सब पर । कॉल करने वाला भी केवल फ़ंक्शन को कॉल कर सकता है और परिणाम पारित कर सकता है।

इसके बारे में सोचो - उपयोग करने के बजाय:

var formattedRate = GetFormattedRate(getRate, rateType);

क्यों नहीं बस का उपयोग करें:

var formattedRate = GetFormattedRate(getRate(rateType));

?

अनावश्यक कोड को कम करने के साथ-साथ यह युग्मन को भी कम करता है - यदि आप बदलना चाहते हैं कि रेट कैसे लाया जाता है (कहते हैं, अगर getRateअब दो तर्कों की आवश्यकता है) तो आपको बदलना नहीं है GetFormattedRate

इसी तरह, लिखने के GetFormattedRate(formatRate, getRate, rateKey)बजाय लिखने का कोई कारण नहीं है formatRate(getRate(rateKey))

चीजों को अधूरा मत छोड़ो।


3
इस मामले में आप सही हैं। लेकिन अगर आंतरिक फ़ंक्शन को कई बार कहा जाता है, तो लूप या मैप फ़ंक्शन में कहें, तो तर्कों में पारित होने की क्षमता उपयोगी होगी। या @ जेक उत्तर में प्रस्तावित के अनुसार कार्यात्मक रचना / करी का उपयोग करें।
user949300

15
@ user949300 हो सकता है, लेकिन यह ओपी के उपयोग का मामला नहीं है (और यदि यह था, तो शायद यह है formatRateकि उन दरों पर मैप किया जाना चाहिए जिन्हें प्रारूपित किया जाना चाहिए)।
जोंशरशेप

4
@ user949300 केवल अपनी भाषा बंद का समर्थन नहीं करता है, तो या जब lamdas बाहर टाइप करने के लिए एक hazzle हैं
Bergi

4
ध्यान दें कि एक फ़ंक्शन और उसके मापदंडों को दूसरे फ़ंक्शन में पास करना एक आलसी शब्दार्थ के बिना किसी भाषा में मूल्यांकन में देरी करने का एक पूरी तरह से वैध तरीका है en.wikipedia.org/wiki/Thunk
Jared Smith

4
@JaredSmith फ़ंक्शन को पास करना, हाँ, इसके मापदंडों को पास करना, केवल तभी जब आपकी भाषा क्लोज़र का समर्थन नहीं करती है।
user253751

15

यदि आपको किसी फ़ंक्शन को फ़ंक्शन में पास करने की आवश्यकता है, क्योंकि यह कुछ अतिरिक्त तर्क देता है या इसे लूप में कहता है, तो आप इसके बजाय एक लंबो पास कर सकते हैं:

public static string GetFormattedRate(
        Func<string> getRate)
{
    var rate = getRate();
    var formattedRate = rate.DollarsPerMonth.ToString("C0");
    return formattedRate;
}

var formattedRate = GetFormattedRate(()=>getRate(rateKey));

लैंबडा उन तर्कों को बांध देगा जो फ़ंक्शन के बारे में नहीं जानते हैं और यह छिपाते हैं कि वे भी मौजूद हैं।


-1

क्या यह वह नहीं है जो आप चाहते हैं?

class RateFormatter
{
    public abstract RateType GetRate(string rateKey);

    public abstract string FormatRate(RateType rate);

    public string GetFormattedRate(string rateKey)
    {
        var rate = GetRate(rateKey);
        var formattedRate = FormatRate(rate);
        return formattedRate;
    }
}

और फिर इसे इस तरह से कॉल करें:

static class Program
{
    public static void Main()
    {
        var rateFormatter = new StandardRateFormatter(connectionStr);
        var formattedRate = rateFormatter.GetFormattedRate(rateKey);

        System.PrintLn(formattedRate);
    }
}

यदि आप एक ऐसी विधि चाहते हैं जो ऑब्जेक्ट ओरिएंटेड भाषा जैसे C # में कई अलग-अलग तरीकों से व्यवहार कर सके, तो ऐसा करने का सामान्य तरीका यह है कि विधि को एक सार पद्धति कहा जाए। यदि आपके पास इसे अलग तरीके से करने का कोई विशिष्ट कारण नहीं है, तो आपको इसे इस तरह से करना चाहिए।

क्या यह एक अच्छे समाधान की तरह दिखता है, या आप सोच रहे हैं कि क्या डाउनसाइड्स हैं?


1
आपके उत्तर में कुछ विचित्र बातें हैं (यदि फ़ॉर्मेटर भी दर प्राप्त कर रहा है, अगर यह सिर्फ एक फ़ॉर्मेटर है? आप GetFormattedRateविधि और बस कॉल को भी हटा सकते हैं IRateFormatter.FormatRate(rate))। मूल अवधारणा हालांकि सही है, और मुझे लगता है कि ओपी को अपने कोड पॉलीमॉर्फिकल को लागू करना चाहिए यदि उसे कई प्रारूपण विधियों की आवश्यकता है।
BgrWorker
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.