সি # তে "ফলন" কীওয়ার্ডের ব্যবহারিক ব্যবহার [বন্ধ]


76

প্রায় 4 বছরের অভিজ্ঞতার পরেও আমি এমন কোনও কোড দেখিনি যেখানে ফলন কীওয়ার্ড ব্যবহৃত হয়। কেউ কি আমাকে এই কীওয়ার্ডের ব্যবহারিক ব্যবহার (ব্যাখ্যা সহ) দেখাতে পারেন এবং যদি তা হয় তবে এটি কী করতে পারে তা পূরণ করার সহজ উপায় ছাড়া অন্য কোনও উপায় নেই?


9
সমস্ত লিনকিউ (বা কমপক্ষে বেশিরভাগ) ফলন ব্যবহার করে প্রয়োগ করা হয়। এছাড়াও ইউনিটি 3 ডি কাঠামো এটির জন্য কিছু ভাল ব্যবহার খুঁজে পেয়েছে - এটি ফাংশনগুলি (ফলন বিবৃতিতে) বিরতি দিতে এবং পরবর্তীকালে আইইনামেবারে রাজ্যটি ব্যবহার করে এটি পুনরায় শুরু করতে ব্যবহৃত হয়।
দানি

2
এটিকে স্ট্যাকওভারফ্লোতে স্থানান্তরিত করা উচিত নয়?
ড্যানি ভারোড

4
@ ড্যানি - এটি স্ট্যাক ওভারফ্লো জন্য উপযুক্ত নয়, কারণ প্রশ্নটি একটি নির্দিষ্ট সমস্যা সমাধান yieldকরতে বলছে না তবে সাধারণভাবে কী ব্যবহার করা যায় সে সম্পর্কে জিজ্ঞাসা করছে ।
ক্রিসএফ

9
বাস্তবিক? আমি কোথায় আমি একটি একক অ্যাপ্লিকেশন ভাবতে পারি না করেন নি এটি ব্যবহৃত।
অ্যারোনআউট

উত্তর:


107

দক্ষতা

yieldশব্দ কার্যকরভাবে সংগ্রহে আইটেম উপর একটি অলস শুমার যে আরো অনেক দক্ষ হতে পারে তৈরি করে। উদাহরণস্বরূপ, যদি আপনার foreachলুপটি 1 মিলিয়ন আইটেমের প্রথম 5 টি আইটেমের উপরে পুনরাবৃত্তি করে তবে তা সবই yieldফিরে আসে এবং আপনি প্রথমে অভ্যন্তরীণভাবে 1 মিলিয়ন আইটেমের সংগ্রহ তৈরি করেন নি। একইভাবে আপনি ব্যবহার করতে চান yieldসঙ্গে IEnumerable<T>আপনার নিজের প্রোগ্রামিং পরিস্থিতিতে আগমন মান একই দক্ষতাসম্পন্ন অর্জন করা।

একটি নির্দিষ্ট দৃশ্যে দক্ষতার উদাহরণ অর্জন

কোনও পুনরুক্তি পদ্ধতি নয়, একটি বড় সংগ্রহের সম্ভাব্য অদক্ষ ব্যবহার,
(মধ্যবর্তী সংগ্রহটি প্রচুর আইটেমযুক্ত তৈরি করা হয়)

// Method returns all million items before anything can loop over them. 
List<object> GetAllItems() {
    List<object> millionCustomers;
    database.LoadMillionCustomerRecords(millionCustomers); 
    return millionCustomers;
}

// MAIN example ---------------------
// Caller code sample:
int num = 0;
foreach(var itm in GetAllItems())  {
    num++;
    if (num == 5)
        break;
}
// Note: One million items returned, but only 5 used. 

Iterator সংস্করণ, দক্ষ
(কোন মধ্যবর্তী সংগ্রহ নির্মিত হয় না)

// Yields items one at a time as the caller's foreach loop requests them
IEnumerable<object> IterateOverItems() {
    for (int i; i < database.Customers.Count(); ++i)
        yield return database.Customers[i];
}

// MAIN example ---------------------
// Caller code sample:
int num = 0;
foreach(var itm in IterateOverItems())  {
    num++;
    if (num == 5)
        break;
}
// Note: Only 5 items were yielded and used out of the million.

কিছু প্রোগ্রামিং পরিস্থিতি সরল করুন

অন্য ক্ষেত্রে, তালিকাগুলি বাছাই এবং একত্রিতকরণের প্রোগ্রামগুলি প্রোগ্রামকে সহজ করে তোলে কারণ আপনি কেবলমাত্র yieldমধ্যবর্তী সংগ্রহগুলিতে বাছাই করা এবং সেগুলিতে সেখানে অদলবদলের পরিবর্তে কাঙ্ক্ষিত ক্রমে আইটেমগুলি ফিরিয়ে আনেন। এরকম অনেক দৃশ্য রয়েছে।

মাত্র একটি উদাহরণ দুটি তালিকার মার্জ করা:

IEnumerable<object> EfficientMerge(List<object> list1, List<object> list2) {
    foreach(var o in list1) 
        yield return o; 
    foreach(var o in list2) 
        yield return o;
}

এই পদ্ধতিটি আইটেমগুলির একটি সংলগ্ন তালিকা ফিরিয়ে দেয়, কার্যকরভাবে কোনও মধ্যবর্তী সংগ্রহের প্রয়োজন নেই a

অধিক তথ্য

yieldশব্দ শুধুমাত্র একটি পুনরুক্তিকারীর পদ্ধতি প্রেক্ষাপটে ব্যবহার করা যাবে (একটি রিটার্ন টাইপ থাকার IEnumerable, IEnumerator, IEnumerable<T>, অথবা IEnumerator<T>।) এবং সেখানে সঙ্গে এক বিশেষ সম্পর্ক রয়েছে foreach। Iteilers বিশেষ পদ্ধতি। দুটিই MSDN ফলন ডকুমেন্টেশন এবং পুনরুক্তিকারীর ডকুমেন্টেশন মজার তথ্য এবং ধারণা ব্যাখ্যা প্রচুর রয়েছে। সঙ্গে এটি সম্পর্কিত করতে ভুলবেন না শব্দ এটা সম্পর্কে খুব পড়া, iterators আপনার বোঝার সম্পূরক দ্বারা।foreach

পুনরুক্তিকারীরা কীভাবে তাদের দক্ষতা অর্জন করে সে সম্পর্কে জানতে, গোপনীয়তা সি # সংকলক দ্বারা উত্পাদিত আইএল কোডে রয়েছে। একটি পুনরুক্তি পদ্ধতির জন্য উত্পন্ন আইএল একটি নিয়মিত (নন-ইটারেটর) পদ্ধতির জন্য উত্পন্ন উত্কর্ষের থেকে খুব আলাদা। এই নিবন্ধ (ফলন কীওয়ার্ডটি আসলে কী উত্পন্ন করে?) এই জাতীয় অন্তর্দৃষ্টি সরবরাহ করে।


2
এগুলি বিশেষত আলগোরিদিমগুলির জন্য কার্যকর যা একটি (সম্ভবত দীর্ঘ) ক্রম নেয় এবং অন্যটি উত্পন্ন করে যেখানে ম্যাপিংটি এক থেকে এক নয়। বহুভুজ ক্লিপিং এর উদাহরণ; যে কোনও নির্দিষ্ট প্রান্তটি একবারে ক্লিপ করলে অনেকগুলি এমনকি কোনও প্রান্ত তৈরি করতে পারে। আইট্রেটাররা এটিকে প্রকাশ করা অত্যন্ত সহজ করে তোলে এবং এগুলি লেখার সর্বোত্তম উপায়গুলির মধ্যে ফলন একটি।
ডোনাল ফেলো

আমি লিখেছি হিসাবে +1 আরও ভাল উত্তর। এখন আমি শিখেছি ফলন আরও ভাল পারফরম্যান্সের জন্য ভাল।
জানু_ভি

3
একসময়, আমি বাইনারি নেটওয়ার্ক প্রোটোকলের জন্য প্যাকেট তৈরিতে ফলন ব্যবহার করি। এটি সি # এর মধ্যে সবচেয়ে প্রাকৃতিক পছন্দ বলে মনে হয়েছিল।
গিরিগিরিস আন্দ্রেসেক

4
না database.Customers.Count()সমগ্র গ্রাহকদের শুমার গনা, এইভাবে প্রতিটি আইটেম মধ্য দিয়ে যেতে আরও দক্ষ কোড প্রয়োজন?
স্টিফেন

5
আমাকে মলদ্বার বলুন, তবে তা সংমিশ্রণ নয়, মার্জিন নয়। (এবং লিনকের ইতিমধ্যে একটি কনক্যাট পদ্ধতি রয়েছে))
ওল্ডফার্ট

4

কিছু সময় আগে আমার একটি ব্যবহারিক উদাহরণ ছিল, আসুন ধরে নেওয়া যাক আপনার মতো পরিস্থিতি রয়েছে:

List<Button> buttons = new List<Button>();
void AddButtons()
{
   for ( int i = 0; i <= 10; i++ ) {
      var button = new Button();
      buttons.Add(button);
      button.Click += (sender, e) => 
          MessageBox.Show(String.Format("You clicked button number {0}", ???));
   }
}

বাটন বস্তুটি সংগ্রহে তার নিজের অবস্থানটি জানে না। একই সীমাবদ্ধতা Dictionary<T>বা অন্যান্য সংগ্রহের ধরণের ক্ষেত্রে প্রযোজ্য ।

yieldকীওয়ার্ড ব্যবহার করে আমার সমাধানটি এখানে দেওয়া হয়েছে :

interface IHasId { int Id { get; set; } }

class IndexerList<T>: List<T>, IEnumerable<T> where T: IHasId
{
   List<T> elements = new List<T>();
   new public void Clear() { elements.Clear(); }
   new public void Add(T element) { elements.Add(element); }
   new public int Count { get { return elements.Count; } }    
   new public IEnumerator<T> GetEnumerator()
   {
      foreach ( T c in elements )
         yield return c;
   }

   new public T this[int index]
   {
      get
      {
         foreach ( T c in elements ) {
            if ( (int)c.Id == index )
               return c;
         }
         return default(T);
      }
   }
}

এবং এটিই আমি এটি ব্যবহার করি:

class ButtonWithId: Button, IHasId
{
   public int Id { get; private set; }
   public ButtonWithId(int id) { this.Id = id; }
}

IndexerList<ButtonWithId> buttons = new IndexerList<ButtonWithId>();
void AddButtons()
{
   for ( int i = 10; i <= 20; i++ ) {
      var button = new ButtonWithId(i);
      buttons.Add(button);
      button.Click += (sender, e) => 
         MessageBox.Show(String.Format("You clicked button number {0}", ( (ButtonWithId)sender ).Id));
   }
}

forসূচকটি খুঁজে পেতে আমাকে আমার সংগ্রহের উপর কোনও লুপ তৈরি করতে হবে না । আমার বোতামটির একটি আইডি রয়েছে এবং এটি সূচক হিসাবেও ব্যবহৃত হয় IndexerList<T>, সুতরাং আপনি কোনও অনর্থক আইডি বা সূচকগুলি এড়াতে পারেন - এটাই আমার পছন্দ! সূচক / আইডি একটি স্বেচ্ছাসেবী সংখ্যা হতে পারে।


2

একটি বাস্তব উদাহরণ এখানে পাওয়া যেতে পারে:

http://www.ytechie.com/2009/02/using-c-yield-for-readability-and-performance.html

মানক কোডের চেয়ে বেশি ফলন ব্যবহারের অনেকগুলি সুবিধা রয়েছে:

  • যদি পুনরুক্তিটি কোনও তালিকা তৈরি করতে ব্যবহৃত হয় তবে আপনি রিটার্ন পেতে পারেন এবং কলকারী সিদ্ধান্ত নিতে পারেন যে তিনি কোনও ফলাফলের তালিকায় চান কিনা, তা নয়।
  • কলকারী আপনি পুনরাবৃত্তিতে যা করছেন তার সুযোগের বাইরে থাকা কারণের জন্য পুনরাবৃত্তি বাতিল করার সিদ্ধান্ত নিতে পারে।
  • কোডটি কিছুটা খাটো।

যাইহোক, জান_ভি যেমন বলেছিলেন (কেবল কয়েক সেকেন্ডের মধ্যে আমাকে এটি মারধর করুন :-) আপনি এটি ছাড়া বাঁচতে পারেন কারণ অভ্যন্তরীণভাবে সংকলক উভয় ক্ষেত্রেই কোডটি প্রায় একই রকম তৈরি করবে।


1

এখানে একটি উদাহরণ:

https://bitbucket.org/ant512/workingweek/src/a745d02ba16f/source/WorkingWeek/Week.cs#cl-158

শ্রেণি একটি কার্যদিবসের উপর ভিত্তি করে তারিখ গণনা সম্পাদন করে। আমি ক্লাসের একটি উদাহরণ বলতে পারি যে বব প্রতি সপ্তাহে 9:30 থেকে 17:30 পর্যন্ত কাজ করে 12:30 এ দুপুরের খাবারের জন্য এক ঘন্টা বিরতি দিয়ে with এই জ্ঞানের সাথে, অ্যাসেন্ডিংশিফ্টস () ফাংশন সরবরাহিত তারিখের মধ্যে কাজের শিফট অবজেক্ট উত্পন্ন করবে। এই বছরের 1 জানুয়ারি থেকে 1 ফেব্রুয়ারির মধ্যে ববের সমস্ত কার্যকরী শিফ্টের তালিকা তৈরি করতে, আপনি এটি এটি ব্যবহার করুন:

foreach (var shift in week.AscendingShifts(new DateTime(2011, 1, 1), new DateTime(2011, 2, 1)) {
    Console.WriteLine(shift);
}

ক্লাসটি সত্যিই কোনও সংগ্রহের মাধ্যমে পুনরাবৃত্তি করে না। তবে দুটি তারিখের মধ্যে স্থানান্তরগুলি সংগ্রহ হিসাবে ভাবা যেতে পারে। yieldঅপারেটর এটা সংগ্রহ নিজেই তৈরি ছাড়াই এই কাল্পনিক সংগ্রহ পুনরুক্তি সম্ভব করে তোলে।


1

আমার কাছে একটি ছোট ডিবি ডেটা স্তর রয়েছে যার একটি commandশ্রেণি রয়েছে যাতে আপনি এসকিউএল কমান্ড পাঠ্য নির্ধারণ করেন, কমান্ড টাইপ করে এবং 'কমান্ড প্যারামিটারগুলি' এর একটি সংখ্যার ফিরিয়ে দেন।

মূলত ধারণাটি হ'ল SqlCommandসমস্ত সময় গুণাবলী এবং পরামিতিগুলি ম্যানুয়ালি পূরণের পরিবর্তে সিএলআর কমান্ড টাইপ করা উচিত।

সুতরাং এখানে এমন একটি ফাংশন রয়েছে যা দেখে মনে হচ্ছে:

IEnumerable<DbParameter> GetParameters()
{
    // here i do something like

    yield return new DbParameter { name = "@Age", value = this.Age };

    yield return new DbParameter { name = "@Name", value = this.Name };
}

এই শ্রেণীর উত্তরাধিকারসূত্রে প্রাপ্ত commandশ্রেণীর বৈশিষ্ট্য রয়েছে Ageএবং Name

তারপরে আপনি কোনও commandবস্তুর নতুন বৈশিষ্ট্যগুলি পূর্ণ করতে পারেন এবং এটি একটি dbইন্টারফেসে পাস করতে পারেন যা আসলে কমান্ড কল করে।

সব মিলিয়ে এসকিউএল কমান্ডের সাথে কাজ করা এবং এগুলি টাইপ করা রাখা সত্যিই সহজ করে তোলে।


1

যদিও মার্জ হওয়া মামলাটি ইতিমধ্যে গৃহীত উত্তরে coveredাকা পড়েছে, তবে আমি আপনাকে ফলন-মার্জ পরম এক্সটেনশন পদ্ধতিটি দেখাচ্ছি show:

public static IEnumerable<T> AppendParams<T>(this IEnumerable<T> a, params T[] b)
{
    foreach (var el in a) yield return el;
    foreach (var el in b) yield return el;
}

নেটওয়ার্ক প্রোটোকলের প্যাকেটগুলি তৈরি করতে আমি এটি ব্যবহার করি:

static byte[] MakeCommandPacket(string cmd)
{
    return
        header
        .AppendParams<byte>(0, 0, 1, 0, 0, 1, 0x92, 0, 0, 0, 0)
        .AppendAscii(cmd)
        .MarkLength()
        .MarkChecksum()
        .ToArray();
}

MarkChecksumপদ্ধতি, উদাহরণস্বরূপ, এরকম দেখায়। এবং এটির yieldএকটিও রয়েছে:

public static IEnumerable<byte> MarkChecksum(this IEnumerable<byte> data, int pos = 6)
{
    foreach (byte b in data)
    {
        yield return pos-- == 0 ? (byte)data.Sum(z => z) : b;
    }
}

কিন্তু একটি পৃথক গণনা প্রক্রিয়া ট্রিগার হিসাবে একটি অঙ্ক পদ্ধতিতে Sum () এর মতো সামগ্রিক পদ্ধতি ব্যবহার করার সময় সতর্কতা অবলম্বন করুন।


1

ইলাস্টিক অনুসন্ধান। নেট উদাহরণ রেপোতে yield returnএকটি নির্দিষ্ট আকারের সাথে একাধিক সংকলনে একটি সংগ্রহকে বিভাগ করার জন্য দুর্দান্ত উদাহরণ রয়েছে :

https://github.com/elastic/elasticsearch-net-example/blob/master/src/NuSearch.Domain/Extensions/PartitionExtension.cs

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> source, int size)
    {
        T[] array = null;
        int count = 0;
        foreach (T item in source)
        {
            if (array == null)
            {
                array = new T[size];
            }
            array[count] = item;
            count++;
            if (count == size)
            {
                yield return new ReadOnlyCollection<T>(array);
                array = null;
                count = 0;
            }
        }
        if (array != null)
        {
            Array.Resize(ref array, count);
            yield return new ReadOnlyCollection<T>(array);
        }
    }

0

জান_ ভি এর উত্তরটি প্রসারিত করে, আমি কেবল এটি সম্পর্কিত একটি বাস্তব-জগতে মামলা করেছি:

আমার ফাইন্ডফার্সফাইল / ফাইন্ডনেক্সটফিলের কার্নেল 32 সংস্করণগুলি ব্যবহার করা দরকার। আপনি প্রথম কলটি থেকে একটি হ্যান্ডেল পান এবং পরবর্তী সমস্ত কলগুলিতে এটি ফিড করুন। এটি একটি গণকের মধ্যে মোড়ক করুন এবং আপনি ভবিষ্যদ্বাণী দিয়ে সরাসরি ব্যবহার করতে পারেন এমন কিছু পান।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.