2011-08-18 20 views
21

मैं सी # में कुछ कार्यात्मक सामग्री का उपयोग कर रहा हूं और इस तथ्य पर अटक गया हूं कि List.Add अद्यतन सूची वापस नहीं करता है।क्या सी के अल्पविराम ऑपरेटर के बराबर idiomatic सी # है?

सामान्यतः, मैं किसी ऑब्जेक्ट पर फ़ंक्शन कॉल करना चाहता हूं और फिर अद्यतन ऑब्जेक्ट को वापस भेजना चाहता हूं।

((accum, data) => accum.Add(data), accum) 

मैं इस तरह मेरे अपने "अल्पविराम ऑपरेटर" लिख सकते हैं::

उदाहरण के लिए यह बहुत अच्छा है, तो सी # एक अल्पविराम ऑपरेटर था होगा

static T comma(Action a, Func<T> result) { 
    a(); 
    return result(); 
} 

ऐसा लगता है कि यह काम करेगा, लेकिन कॉल साइट बदसूरत होगी। मेरा पहला उदाहरण कुछ ऐसा होगा:

((accum, data) => comma(accum.Add(data),()=>accum)) 

पर्याप्त उदाहरण! बाद में आने वाले किसी अन्य डेवलपर के बिना ऐसा करने का सबसे साफ तरीका क्या है और कोड की गंध पर उसकी नाक झुर्रियां कर रही हैं?

+0

List.Add एक नई सूची वापस नहीं करता है लेकिन सिर्फ यथा-स्थान के लिए उसे संशोधित: जब एक flunt शैली में है कि टुकड़ा लिखने के लिए प्रयास करता है

समझ वाक्य रचना का सच योग्यता स्पष्ट है। इस अर्थ में, यह कार्यात्मक नहीं है। –

उत्तर

1

आप सी # 3.0 में कोड ब्लॉक का उपयोग करके लगभग बिल्कुल पहला उदाहरण कर सकते हैं।

((accum, data) => { accum.Add(data); return accum; }) 
14

मुझे यह Fluent के रूप में पता है।

एक List.Add का एक सुविज्ञ उदाहरण एक्सटेंशन के तरीके

static List<T> MyAdd<T>(this List<T> list, T element) 
{ 
    list.Add(element); 
    return list; 
} 
+2

फ्लुएंट परिभाषा सहित अच्छी कॉल। –

+0

लेकिन आप इस तरह की चीज़ के लिए LINQ का उपयोग क्यों नहीं करेंगे? –

+0

@ केविनरोच: कोई भी सुझाव नहीं दे रहा है कि आप linq का उपयोग नहीं करते हैं। वास्तव में यह विधि linq के साथ अच्छी तरह से एकीकृत करने के लिए प्रतीत होता है। – recursive

2

का उपयोग कर विस्तार विधि यकीनन सबसे अच्छा समाधान है, लेकिन पूर्णता 'खातिर, स्पष्ट विकल्प मत भूलना: एक आवरण वर्ग।

public class FList<T> : List<T> 
{ 
    public new FList<T> Add(T item) 
    { 
     base.Add(item); 
     return this; 
    } 

    public new FList<T> RemoveAt(int index) 
    { 
     base.RemoveAt(index); 
     return this; 
    } 

    // etc... 
} 

{ 
    var list = new FList<string>(); 
    list.Add("foo").Add("remove me").Add("bar").RemoveAt(1); 
} 
2

मैंने सोचा कि यह की आवश्यकता नहीं है कि आप आवरण तरीकों बारे में मेरी आवरण वर्ग जवाब का एक संस्करण बनाने के लिए दिलचस्प हो जाएगा।

public class FList<T> : List<T> 
{ 
    public FList<T> Do(string method, params object[] args) 
    { 
     var methodInfo = GetType().GetMethod(method); 

     if (methodInfo == null) 
      throw new InvalidOperationException("I have no " + method + " method."); 

     if (methodInfo.ReturnType != typeof(void)) 
      throw new InvalidOperationException("I'm only meant for void methods."); 

     methodInfo.Invoke(this, args); 
     return this; 
    } 
} 

{ 
    var list = new FList<string>(); 

    list.Do("Add", "foo") 
     .Do("Add", "remove me") 
     .Do("Add", "bar") 
     .Do("RemoveAt", 1) 
     .Do("Insert", 1, "replacement"); 

    foreach (var item in list) 
     Console.WriteLine(item);  
} 

आउटपुट:

foo 
replacement 
bar 

संपादित

आप कर सकते हैं सी # अनुक्रमित गुण का दुरुपयोग करके वाक्य रचना नीचे पतला।

बस इस पद्धति जोड़ें:

public FList<T> this[string method, params object[] args] 
{ 
    get { return Do(method, args); } 
} 

और कॉल अब की तरह दिखता है:

list = list["Add", "foo"] 
      ["Add", "remove me"] 
      ["Add", "bar"] 
      ["RemoveAt", 1] 
      ["Insert", 1, "replacement"]; 
लाइनब्रेक वैकल्पिक जा रहा है, निश्चित रूप से साथ

सिंटैक्स हैकिंग करने में बस थोड़ा मज़ा है।

+2

मैंने कभी इस तरह की इंडेक्स संपत्ति का उपयोग करने का विचार नहीं किया होगा।एक ही समय में अद्भुत और सबसे बदसूरत;) (2 कारणों से: ऑब्जेक्ट को {} प्रॉपर्टी में बदलना, और जादू तारों का उपयोग करना) – devio

+0

@devio, मैं कुरूपता पर विवाद नहीं करूंगा (लेकिन यह भी ठंडा है :)), लेकिन ये जादू तार नहीं हैं। जादू तार कुछ ऐसा है जो विशेष/अद्वितीय परिणाम उत्पन्न करता है। लेकिन इस तरह हार्डकोडेड अक्षर का उपयोग करके दोनों विधि नामों (कोई प्रतीकों!) और प्रकारों की संकलन-समय की जांच को बाईपास नहीं किया जाता है। यह थोड़ी सी गतिशील टाइप गतिशील भाषा में सी # को बदलता है (नरक धीमा नहीं है!)। मज़ा :) –

+1

आप विधि नामों के लिए एक एनम का उपयोग कर सकते हैं (रनटाइम पर स्ट्रिंग में कनवर्ट करें) - यह कोई तकनीकी अंतर नहीं बनाता है लेकिन यह साफ-सुथरा और बहुत कम त्रुटि-प्रवण होगा। (या आप consts का उपयोग कर सकते हैं) –

3

यह कंसैट http://msdn.microsoft.com/en-us/library/vstudio/bb302894%28v=vs.100%29.aspx है। बस एक सरणी में एक ही आइटम लपेटें। कार्यात्मक कोड मूल डेटा को म्यूट नहीं करना चाहिए। यदि प्रदर्शन एक चिंता है, और यह पर्याप्त नहीं है, तो आप अब कार्यात्मक प्रतिमान का उपयोग नहीं करेंगे।

((accum, data) => accum.Concat(new[]{data})) 
+1

मुझे जवाब के LINQ-y-est लग रहा था। * shrug * – shelleybutterfly

2

मुझे पता है कि इस सूत्र बहुत पुराना है, लेकिन मैं भविष्य उपयोगकर्ताओं के लिए निम्नलिखित जानकारी संलग्न करना चाहते हैं:

वर्तमान में इस तरह के एक ऑपरेटर नहीं है।

int square = (int x = int.Parse(Console.ReadLine()); Console.WriteLine(x - 2); x * x); 

जो इस प्रकार के रूप में अनुवाद किया जा सकता है:

int square = compiler_generated_Function(); 

[MethodImpl(MethodImplOptions.AggressiveInlining)] 
private int compiler_generated_Function() 
{ 
    int x = int.Parse(Console.ReadLine()); 

    Console.WriteLine(x - 2); 

    return x * x; 
} 

हालांकि, यह सुविधा अंतिम सी # रिलीज होने से पहले हटा दिया गया था सी # 6 विकास चक्र के दौरान एक semicolon operator के रूप में, जोड़ा गया है।

+0

मैं इस नमूना को VS2015 अद्यतन 2 में संकलित नहीं कर सकता, फ्रेमवर्क 4.6.1 को लक्षित करता हूं और स्पष्ट रूप से भाषा संस्करण को सी # 6 में सेट नहीं कर रहा हूं: प्रोग्राम.cs (9,27,9,30): त्रुटि CS1525: अमान्य अभिव्यक्ति अवधि 'int' प्रोग्राम.cs (9,31,9,32): त्रुटि CS1026:) अपेक्षित प्रोग्राम.cs (9,31,9,32): त्रुटि CS1002:; अपेक्षित प्रोग्राम.cs (9, 9 6, 9 7): त्रुटि CS1002:; अपेक्षित प्रोग्राम.cs (9, 9 6, 9 7 9): त्रुटि CS1513:} अपेक्षित मैं क्या गलत कर रहा हूं? – ISanych

+2

यह सुविधा सी # 6 से गिरा दी गई थी। यह रिलीज़ संस्करण में कभी उपलब्ध नहीं था। – Athari

0

एक और तकनीक, सीधे कार्यात्मक प्रोग्रामिंग से, निम्नानुसार है। इस तरह की एक आईओ struct को परिभाषित करें:

/// <summary>TODO</summary> 
public struct IO<TSource> : IEquatable<IO<TSource>> { 
    /// <summary>Create a new instance of the class.</summary> 
    public IO(Func<TSource> functor) : this() { _functor = functor; } 

    /// <summary>Invokes the internal functor, returning the result.</summary> 
    public TSource Invoke() => (_functor | Default)(); 

    /// <summary>Returns true exactly when the contained functor is not null.</summary> 
    public bool HasValue => _functor != null; 

    X<Func<TSource>> _functor { get; } 

    static Func<TSource> Default => null; 
} 

और यह इन विस्तार के तरीकों के साथ एक LINQ करने योग्य इकाई बनाने:

[SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")] 
public static class IO { 
    public static IO<TSource> ToIO<TSource>(this Func<TSource> source) { 
     source.ContractedNotNull(nameof(source)); 
     return new IO<TSource>(source); 
    } 

    public static IO<TResult> Select<TSource,TResult>(this IO<TSource> @this, 
     Func<TSource,TResult> projector 
    ) => 
     @this.HasValue && projector!=null 
      ? New(() => projector(@this.Invoke())) 
      : Null<TResult>(); 

    public static IO<TResult> SelectMany<TSource,TResult>(this IO<TSource> @this, 
     Func<TSource,IO<TResult>> selector 
    ) => 
     @this.HasValue && selector!=null 
      ? New(() => selector(@this.Invoke()).Invoke()) 
      : Null<TResult>(); 

    public static IO<TResult> SelectMany<TSource,T,TResult>(this IO<TSource> @this, 
     Func<TSource, IO<T>> selector, 
     Func<TSource,T,TResult> projector 
    ) => 
     @this.HasValue && selector!=null && projector!=null 
      ? New(() => { var s = @this.Invoke(); return projector(s, selector(s).Invoke()); }) 
      : Null<TResult>(); 

    public static IO<TResult> New<TResult> (Func<TResult> functor) => new IO<TResult>(functor); 

    private static IO<TResult> Null<TResult>() => new IO<TResult>(null); 
} 

और अब आप LINQ व्यापक वाक्य रचना इस प्रकार का उपयोग कर सकते हैं:

using Xunit; 
[Fact] 
public static void IOTest() { 
    bool isExecuted1 = false; 
    bool isExecuted2 = false; 
    bool isExecuted3 = false; 
    bool isExecuted4 = false; 
    IO<int> one = new IO<int>(() => { isExecuted1 = true; return 1; }); 
    IO<int> two = new IO<int>(() => { isExecuted2 = true; return 2; }); 
    Func<int, IO<int>> addOne = x => { isExecuted3 = true; return (x + 1).ToIO(); }; 
    Func<int, Func<int, IO<int>>> add = x => y => { isExecuted4 = true; return (x + y).ToIO(); }; 

    var query1 = (from x in one 
        from y in two 
        from z in addOne(y) 
        from _ in "abc".ToIO() 
        let addOne2 = add(x) 
        select addOne2(z) 
       ); 
    Assert.False(isExecuted1); // Laziness. 
    Assert.False(isExecuted2); // Laziness. 
    Assert.False(isExecuted3); // Laziness. 
    Assert.False(isExecuted4); // Laziness. 
    int lhs = 1 + 2 + 1; 
    int rhs = query1.Invoke().Invoke(); 
    Assert.Equal(lhs, rhs); // Execution. 

    Assert.True(isExecuted1); 
    Assert.True(isExecuted2); 
    Assert.True(isExecuted3); 
    Assert.True(isExecuted4); 
} 

जब कोई आईओ मोनैड चाहता है जो लिखता है लेकिन केवल शून्य देता है, तो इस संरचना को परिभाषित करें और निर्भर करें ईएनटी तरीके:

public struct Unit : IEquatable<Unit>, IComparable<Unit> { 
    [CLSCompliant(false)] 
    public static Unit _ { get { return _this; } } static Unit _this = new Unit(); 
} 

public static IO<Unit> ConsoleWrite(object arg) => 
    ReturnIOUnit(() => Write(arg)); 

public static IO<Unit> ConsoleWriteLine(string value) => 
    ReturnIOUnit(() => WriteLine(value)); 

public static IO<ConsoleKeyInfo> ConsoleReadKey() => new IO<ConsoleKeyInfo>(() => ReadKey()); 

जो आसानी से इस तरह कोड के टुकड़े के लेखन अनुमति देते हैं:

from pass in Enumerable.Range(0, int.MaxValue) 
let counter = Readers.Counter(0) 
select (from state in gcdStartStates 
     where _predicate(pass, counter()) 
     select state) 
into enumerable 
where (from _ in Gcd.Run(enumerable.ToList()).ToIO() 
     from __ in ConsoleWrite(Prompt(mode)) 
     from c in ConsoleReadKey() 
     from ___ in ConsoleWriteLine() 
     select c.KeyChar.ToUpper() == 'Q' 
    ).Invoke() 
select 0; 

जहां वर्ष सी अल्पविराम ऑपरेटर आसानी से यह क्या है के लिए मान्यता प्राप्त है: एक monadic लिखें ऑपरेशन।

(Enumerable.Range(0,int.MaxValue) 
      .Select(pass => new {pass, counter = Readers.Counter(0)}) 
      .Select(_ => gcdStartStates.Where(state => _predicate(_.pass,_.counter())) 
              .Select(state => state) 
        ) 
).Where(enumerable => 
    ((Gcd.Run(enumerable.ToList())).ToIO() 
     .SelectMany(_ => ConsoleWrite(Prompt(mode)),(_,__) => new {}) 
     .SelectMany(_ => ConsoleReadKey(),   (_, c) => new {c}) 
     .SelectMany(_ => ConsoleWriteLine(),  (_,__) => _.c.KeyChar.ToUpper() == 'Q') 
    ).Invoke() 
).Select(list => 0); 
संबंधित मुद्दे