C#

2013-08-15 10 views
6

için uzun bir görüşme için ilerleme bildirme nasıl yapılır? AnalysisResult adlı özel bir dizi özel nesnem var. Dizi yüz binlerce nesneden oluşabilir; ve bazen bu dizinin sadece Distinct() elemanına ihtiyacım var.C#

public static AnalysisResult[] GetDistinct(AnalysisResult[] results) 
{ 
    return results.Distinct(new AnalysisResultDistinctItemComparer()).ToArray(); 
} 

Buradaki sorun dizisi özellikle büyük olduğu zaman bu çağrı (dakika mertebesinde) uzun bir zaman alabilir olmasıdır (: Yani, bir madde karşılaştırıcısı sınıf AnalysisResultDistinctItemComparer denilen ve böyle çağrımı yapmak yazdı 200.000'den fazla nesne).

Şu anda bir arka plan çalışanında bu yöntemi çağırın ve kullanıcıya yöntemin gerçekleştirildiğini bildirmek ve uygulamanın donmamış olduğunu bildirmek için bir döndürme gifini görüntüleyin. Bu her şey yolunda ve iyi ama kullanıcı mevcut ilerlemenin herhangi bir göstergesi değildir.

Kullanıcıya bu eylemin şu andaki ilerleyişini gösterebilmem gerekiyor; ama, iyi bir yaklaşım geliştiremedim.

public static AnalysisResult[] GetDistinct(AnalysisResult[] results) 
{ 
    var query = results.Distinct(new AnalysisResultDistinctItemComparer()); 

    List<AnalysisResult> retVal = new List<AnalysisResult>(); 
    foreach(AnalysisResult ar in query) 
    { 
     // Show progress here 
     retVal.Add(ar); 
    } 

    return retVal.ToArray(); 
} 

Ama sorun benim gerçek ilerleme ne bilmenin bir yolu olması: Böyle bir şey yapmanın oynuyordu. Düşünceler? Öneriler?

+0

Yapacağınızı düşündüğüm sorun, ilerleme göstergesinin maksimum değerini ayarlamak için önünüzde kaç farklı değerin bulunduğunu bilmeniz gerektiğidir. Tabii ki sorgunuz gerçekten kaçana kadar bu değeri bilmeyeceksiniz ... Bu süreci her zaman biraz parallalizm (çerçevenizden emin değil) kullanarak deneyebilirsiniz. Http://msdn.microsoft.com/tr -us/library/dd383943.aspx – Damon

cevap

4

Yöntemin sonunda ToArray()'u arama, sadece yield return kullanın.

public static IEnumerable<AnalysisResult> Distinct(AnalysisResult[] results) 
{ 
    var query = results.Distinct(new AnalysisResultDistinctItemComparer()); 

    foreach(AnalysisResult ar in query) 
    { 
     // Use yield return here, so that the iteration remains lazy. 
     yield return ar; 
    } 
} 

Temelde, yield return yineleme tembel kalmasını sağlamak için bazı derleyici büyü yapar, bu nedenle arayana dönmeden önce oluşturulacak komple yeni koleksiyonu için beklemek gerekmez: Yani bunu. Bunun yerine, her bir öğe hesaplandığında, o ürüne numaralı ürünü hemen ürününe iade edersiniz (daha sonra gerekirse öğeye ilişkin güncelleme mantığını gerçekleştirebilir). Aynı tekniği GetDistinct yönteminizde de kullanabilirsiniz. işte o çiftleri izin vermeyecek şekilde inşa edilen bir HashSet, kullandığı

public static IEnumerable<TSource> DistinctBy<TSource, TKey> 
    (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) 
{ 
    HashSet<TKey> seenKeys = new HashSet<TKey>(); 
    foreach (TSource element in source) 
    { 
     if (seenKeys.Add(keySelector(element))) 
     { 
      yield return element; 
     } 
    } 
} 

Bildirimi:

Jon Skeet bu (LINQ's Distinct() on a particular property) benzeyen bir uygulama vardır. Sadece öğenin önceden eklenip eklenmediğini kontrol edin ve değilse geri verin.

Tüm bunların, bir Algoritma ve Veri Yapıları türü sorusu olduğunu unutmayın. Böyle bir şey yapmak çok daha kolay olurdu:

olduğunu
Dictionary<Key, Value> distinctItems = new Dictionary<Key, Value>(); 

foreach (var item in nonDistinctSetOfItems) { 
    if (distinctItems.ConatainsKey(item.KeyProperty) == false) { 
     distinctItems.Add(item.KeyProperty, item); 
    } 
} 

... = distinctItems.Values // This would contain only the distinct items. 

, bir Sembol Tablosu/Dictionary problemin sadece bu tür için üretilmiştir - benzersiz anahtarlar ile girdileri ilişkilendirerek. Verilerinizi bu şekilde saklarsanız, sorunu büyük ölçüde basitleştirir. Basit çözümü göz ardı etmeyin!

+0

Jon'un Distinct nesneleri alma yaklaşımı hakkındaki bilgiler için teşekkürler. Benimki benzer ama onun daha iyi. :-) –

+0

Bu cevabı üç nedenden dolayı kabul ediyorum. 1) Farklı öğeleri belirlemek için üç yaklaşım sağladınız. 2) İlk iki yaklaşım, "ilerlemeyi" belirlemede yardımcı olmadıkları ayrı öğeleri verirken; Ancak, üçüncü yaklaşım elbette Farklı öğeleri çekme sürecinde nerede olduğumuzu bilmemi sağlıyor. 3) Kafamı karanlık bir yere soktum ve üçüncü yaklaşım kadar basit bir şey yapmayı bile düşünmedim. –

+0

Hey, çok minnettarım. Ama evet, yeni araçlar harika olsa da, klasik tekniklerin bir sebepten dolayı klasik olduğunu hatırlamak iyidir! – sircodesalot

1

Bu Distinct yönteminin tasarımı göz önüne alındığında, Distinct adını verdiğiniz her koleksiyonda yineleniyorsunuz. Diziye bir nesne eklediğinizde bir dizine ekleyen özel bir koleksiyon yazmayı düşündünüz mü?

+0

İlginç bir yaklaşım. Hem değiştirilmemiş orijinal diziye hem de farklı diziye referanslar tutmam gerekir. Bunun bir örneği var mı? –

+0

Bir örneğim yok, ancak kavram sorgudan ziyade ekleme üzerinde değerlendirme yapmaktır. Karşılaştırmanız düzeltildiyse, endeks yöntemi çoğaltmayı önlemek için bir referans indeksi kullanabilir. @sircodesalot'tan elde edilen verim dönüşü yöntemi, daha esnek olup, tasarım aşamasında lambda ile karşılaştırmayı belirleme olanağı sunar. –

0

Diğer taraftan, "Distinct" ve "DisplayProgress" işlerinizi birden çok iş parçacığı ile çalıştırmak için ThreadPool ve WaitHandle'ı kullanabilirsiniz.

public class Sample 
{ 
    public void Run() 
    { 
     var state = new State(); 
     ThreadPool.QueueUserWorkItem(DoWork, state); 
     ThreadPool.QueueUserWorkItem(ShowProgress, state); 
     WaitHandle.WaitAll(new WaitHandle[] {state.AutoResetEvent}); 
     Console.WriteLine("Completed"); 
    } 

    public void DoWork(object state) 
    { 
     //do your work here 
     for (int i = 0; i < 10; i++) 
     { 
      ((State) state).Status++; 
      Thread.Sleep(1000); 
     } 

     ((State) state).AutoResetEvent.Set(); 
    } 

    public void ShowProgress(object state) 
    { 
     var s = (State) state; 
     while (!s.IsCompleted()) 
     { 

      if (s.PrintedStatus != s.Status) 
       Console.WriteLine(s.Status); 
      s.PrintedStatus = s.Status; 
     } 
    } 

    public class State 
    { 
     public State() 
     { 
      AutoResetEvent = new AutoResetEvent(false); 
     } 

     public AutoResetEvent AutoResetEvent { get; private set; } 
     public int Status { get; set; } 
     public int PrintedStatus { get; set; } 
     private bool _completed; 
     public bool IsCompleted() 
     { 
      return _completed; 
     } 
     public void Completed() 
     { 
      _completed = true; 
      AutoResetEvent.Set(); 
     } 
    } 
}