Anda harus instantiate semua elemen dalam enumerasi sehingga Anda dapat membalik urutannya. Itu mungkin tidak mungkin. Pertimbangkan urutannya: IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }Bagaimana Anda membalikkan itu?
Suncat2000
Jawaban:
84
Ketika bekerja dengan daftar (pengindeksan langsung), Anda tidak dapat melakukannya seefisien menggunakan forloop.
Sunting: Yang secara umum berarti, ketika Anda dapat menggunakan forperulangan, kemungkinan metode yang tepat untuk tugas ini. Plus, sebanyak foreachditerapkan dalam urutan, konstruksi itu sendiri dibangun untuk mengekspresikan loop yang independen dari indeks elemen dan urutan iterasi, yang sangat penting dalam pemrograman paralel . Ini adalah pendapat saya bahwa iterasi mengandalkan agar tidak harus menggunakan foreachuntuk perulangan.
Saya menemukan pernyataan terakhir Anda agak terlalu umum. Tentunya ada kasus di mana, misalnya, suatu IEnumerable dari beberapa jenis perlu diulang melalui agar? Apakah Anda tidak menggunakan foreach dalam kasus itu? Apa yang akan Anda gunakan
avl_sweden
1
UPDATE: Lihat [Jawaban Bryan untuk pertanyaan serupa [( stackoverflow.com/a/3320924/199364 ), untuk jawaban yang lebih modern, menggunakan Linq, dan mendiskusikan kedua daftar, dan enumerable lainnya.
ToolmakerSteve
UPDATE: Jon Skeet memiliki jawaban yang bahkan lebih elegan, yang sekarang mungkin, menggunakan " yield return".
ToolmakerSteve
2
Saya memiliki reaksi awal yang sama dengan @avl_sweden - "iterasi yang mengandalkan pesanan tidak boleh menggunakan foreach" terdengar terlalu luas. Ya, foreach baik untuk mengekspresikan tugas paralel independen-pesanan. TETAPI juga merupakan bagian penting dari pemrograman berbasis iterator modern . Mungkin intinya adalah bahwa akan lebih jelas / lebih aman jika ada dua kata kunci yang berbeda , untuk memperjelas apakah seseorang menegaskan bahwa iterasi itu tidak tergantung pesanan? [Diberi bahasa seperti Eiffel yang dapat menyebarkan kontrak, pernyataan seperti itu bisa dibuktikan benar atau salah, untuk kode yang diberikan.]
ToolmakerSteve
2
Gagasan bahwa foreach "adalah membangun untuk mengekspresikan loop yang independen dari indeks elemen dan urutan iterasi" tidak benar. Spesifikasi bahasa c # mensyaratkan agar elemen proses memeriksa secara berurutan. Baik dengan menggunakan MoveNext di iterator atau dengan memproses indeks mulai dari nol dan bertambah satu pada setiap iterasi array.
Donald Rich
145
Jika Anda menggunakan .NET 3.5, Anda dapat melakukan ini:
IEnumerable<int> enumerableThing =...;foreach(var x in enumerableThing.Reverse())
Ini tidak terlalu efisien karena pada dasarnya harus melalui pencacah ke depan meletakkan segala sesuatu di atas tumpukan kemudian muncul semuanya kembali dalam urutan terbalik.
Jika Anda memiliki koleksi yang dapat diindeks secara langsung (mis. IList), Anda harus menggunakan forloop.
Jika Anda menggunakan .NET 2.0 dan tidak dapat menggunakan for loop (yaitu Anda hanya memiliki IEnumerable) maka Anda hanya perlu menulis fungsi Reverse Anda sendiri. Ini seharusnya bekerja:
Ini bergantung pada beberapa perilaku yang mungkin tidak terlalu jelas. Ketika Anda melewati sebuah IEnumerable ke konstruktor stack itu akan beralih melalui itu dan mendorong item ke tumpukan. Ketika Anda kemudian beralih melalui tumpukan itu muncul hal-hal kembali dalam urutan terbalik.
Ini dan Reverse()metode ekstensi .NET 3.5 jelas akan meledak jika Anda memberinya IEnumerable yang tidak pernah berhenti mengembalikan item.
Apakah saya melewatkan sesuatu atau apakah solusi .Net 3.5 Anda tidak benar-benar berfungsi? Membalikkan () membalikkan daftar di tempat dan tidak mengembalikannya. Sayang sekali, karena saya berharap untuk solusi seperti itu.
@ user12861, Micky Duncan: jawabannya tidak salah, Anda kehilangan sesuatu. Ada metode Reverse pada System.Collections.Generic.List <T> yang melakukan reverse di tempat. Di. Net 3.5 ada metode ekstensi pada IEnumerable <T> bernama Reverse. Saya telah mengubah contoh dari var ke IEnumerable <int> untuk menjadikan ini lebih eksplisit.
Matt Howells
55
Seperti yang dikatakan 280Z28, IList<T>Anda hanya bisa menggunakan indeks. Anda dapat menyembunyikan ini dalam metode ekstensi:
publicstaticIEnumerable<T>FastReverse<T>(thisIList<T> items){for(int i = items.Count-1; i >=0; i--){yieldreturn items[i];}}
Ini akan lebih cepat daripada Enumerable.Reverse()yang buffer semua data terlebih dahulu. (Saya tidak yakin Reverseada optimasi yang diterapkan seperti itu Count().) Perhatikan bahwa buffering ini berarti bahwa data dibaca sepenuhnya ketika Anda pertama kali memulai iterasi, sedangkan FastReverseakan "melihat" setiap perubahan yang dibuat ke daftar saat Anda iterate. (Ini juga akan pecah jika Anda menghapus beberapa item di antara iterasi.)
Untuk urutan umum, tidak ada cara untuk mengulang secara terbalik - urutannya bisa tak terbatas, misalnya:
publicstaticIEnumerable<T>GetStringsOfIncreasingSize(){string ret ="";while(true){yieldreturn ret;
ret = ret +"x";}}
Apa yang Anda harapkan terjadi jika Anda mencoba mengulanginya secara terbalik?
Hanya rasa ingin tahu, mengapa menggunakan "> = 0" bukannya "> -1"?
Chris S
15
> mengapa menggunakan "> = 0" bukannya "> -1"? Karena> = 0 lebih baik mengkomunikasikan maksud kepada manusia membaca kode. Kompiler harus dapat mengoptimalkannya ke yang setara> -1 jika hal itu akan meningkatkan kinerja.
Mark Maslar
1
FastReverse (item IList ini <T>) harus FastReverse <T> (item IList ini <T>. :)
Rob
Tongkat pemisah 0 <= i bahkan lebih baik. Setelah mengajar cukup banyak matematika anak-anak selama bertahun-tahun, dan membantu orang dengan pengkodean, saya menemukan bahwa itu jauh mengurangi kesalahan yang dibuat orang jika mereka SELALU bertukar a> b ke b <a ketika segala sesuatunya muncul dalam urutan alami sebuah garis angka (Atau sumbu X dari sistem koordinat) - satu-satunya kelemahan adalah jika Anda perlu menempelkan persamaan ke dalam XML, ucapkan .config
Eske Rahn
13
Sebelum menggunakan foreachuntuk iterasi, balikkan daftar dengan reversemetode:
myList.Reverse();foreach(List listItem in myList){Console.WriteLine(listItem);}
Sepatah kata hati tentang apa myListyang akan membantu. IEnumerable.Reverse tidak bekerja di sini.
nawfal
2
@ MA-Maddin tidak keduanya berbeda. Saya kira penjawabnya mengandalkan Daftar <T>. Balikkan yang sudah ada.
nawfal
Sebenarnya, saya tidak tahu mengapa saya mengatakan itu. Saya seharusnya menambahkan rincian lebih lanjut ... Pokoknya ini adalah kode yang membingungkan karena myListtampaknya bertipe System.Collections.Generic.List<System.Windows.Documents.List>(atau Listjenis kustom lainnya ) kalau tidak, kode ini tidak berfungsi: P
Martin Schneider
5
Kadang-kadang Anda tidak memiliki kemewahan pengindeksan, atau mungkin Anda ingin membalikkan hasil kueri Linq, atau mungkin Anda tidak ingin mengubah kumpulan sumber, jika ada yang benar, Linq dapat membantu Anda.
Metode ekstensi Linq menggunakan tipe anonim dengan Linq Select untuk memberikan kunci penyortiran untuk Linq OrderByDescending;
var eable =new[]{"a","b","c"};foreach(var o in eable.Invert()){Console.WriteLine(o);}// "c", "b", "a"
Itu bernama "Balikkan" karena ini identik dengan "Balikkan" dan memungkinkan disambiguasi dengan implementasi Daftar Balikkan.
Dimungkinkan untuk membalikkan rentang koleksi tertentu juga, karena Int32.MinValue dan Int32.MaxValue berada di luar kisaran jenis indeks pengumpulan apa pun, kami dapat memanfaatkannya untuk proses pemesanan; jika indeks elemen di bawah rentang yang diberikan, itu diberikan Int32.MaxValue sehingga pesanannya tidak berubah saat menggunakan OrderByDescending, sama halnya, elemen pada indeks yang lebih besar dari rentang yang diberikan, akan diberikan Int32.MinValue, sehingga mereka muncul di akhir proses pemesanan. Semua elemen dalam rentang yang diberikan diberi indeks normal dan dibalik sesuai.
publicstaticIEnumerable<T>Invert<T>(thisIEnumerable<T> source,int index,int count){var transform = source.Select((o, i)=>new{Index= i < index ?Int32.MaxValue: i >= index + count ?Int32.MinValue: i,Object= o
});return transform.OrderByDescending(o => o.Index).Select(o => o.Object);}
Pemakaian:
var eable =new[]{"a","b","c","d"};foreach(var o in eable.Invert(1,2)){Console.WriteLine(o);}// "a", "c", "b", "d"
Saya tidak yakin dengan kinerja hit implementasi Linq ini versus menggunakan Daftar sementara untuk membungkus koleksi untuk dibalik.
Hal ini mungkin jika Anda dapat mengubah kode koleksi yang menerapkan IEnumerable atau IEnumerable (misalnya implementasi sendiri dari IList).
Buat Iterator melakukan pekerjaan ini untuk Anda, misalnya seperti implementasi berikut melalui antarmuka IEnumerable (dengan asumsi 'item' adalah bidang Daftar dalam sampel ini):
publicIEnumerator<TObject>GetEnumerator(){for(var i = items.Count-1; i >=0; i--){yieldreturn items[i];}}IEnumeratorIEnumerable.GetEnumerator(){returnGetEnumerator();}
Karena ini Daftar Anda akan diulang dalam urutan terbalik melalui daftar Anda.
Sekedar petunjuk: Anda harus dengan jelas menyatakan perilaku khusus dari daftar ini di dalam dokumentasi (bahkan lebih baik dengan memilih nama kelas yang menjelaskan sendiri seperti Stack atau Queue, juga).
Ini buruk karena .Reverse()sebenarnya memodifikasi daftar.
Colin Basnett
-8
Ini bekerja dengan cukup baik
List<string>list=newList<string>();list.Add("Hello");list.Add("Who");list.Add("Are");list.Add("You");foreach(String s inlist){Console.WriteLine(list[list.Count-list.IndexOf(s)-1]);}
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
Bagaimana Anda membalikkan itu?Jawaban:
Ketika bekerja dengan daftar (pengindeksan langsung), Anda tidak dapat melakukannya seefisien menggunakan
for
loop.Sunting: Yang secara umum berarti, ketika Anda dapat menggunakan
for
perulangan, kemungkinan metode yang tepat untuk tugas ini. Plus, sebanyakforeach
diterapkan dalam urutan, konstruksi itu sendiri dibangun untuk mengekspresikan loop yang independen dari indeks elemen dan urutan iterasi, yang sangat penting dalam pemrograman paralel . Ini adalah pendapat saya bahwa iterasi mengandalkan agar tidak harus menggunakanforeach
untuk perulangan.sumber
yield return
".Jika Anda menggunakan .NET 3.5, Anda dapat melakukan ini:
Ini tidak terlalu efisien karena pada dasarnya harus melalui pencacah ke depan meletakkan segala sesuatu di atas tumpukan kemudian muncul semuanya kembali dalam urutan terbalik.
Jika Anda memiliki koleksi yang dapat diindeks secara langsung (mis. IList), Anda harus menggunakan
for
loop.Jika Anda menggunakan .NET 2.0 dan tidak dapat menggunakan for loop (yaitu Anda hanya memiliki IEnumerable) maka Anda hanya perlu menulis fungsi Reverse Anda sendiri. Ini seharusnya bekerja:
Ini bergantung pada beberapa perilaku yang mungkin tidak terlalu jelas. Ketika Anda melewati sebuah IEnumerable ke konstruktor stack itu akan beralih melalui itu dan mendorong item ke tumpukan. Ketika Anda kemudian beralih melalui tumpukan itu muncul hal-hal kembali dalam urutan terbalik.
Ini dan
Reverse()
metode ekstensi .NET 3.5 jelas akan meledak jika Anda memberinya IEnumerable yang tidak pernah berhenti mengembalikan item.sumber
Seperti yang dikatakan 280Z28,
IList<T>
Anda hanya bisa menggunakan indeks. Anda dapat menyembunyikan ini dalam metode ekstensi:Ini akan lebih cepat daripada
Enumerable.Reverse()
yang buffer semua data terlebih dahulu. (Saya tidak yakinReverse
ada optimasi yang diterapkan seperti ituCount()
.) Perhatikan bahwa buffering ini berarti bahwa data dibaca sepenuhnya ketika Anda pertama kali memulai iterasi, sedangkanFastReverse
akan "melihat" setiap perubahan yang dibuat ke daftar saat Anda iterate. (Ini juga akan pecah jika Anda menghapus beberapa item di antara iterasi.)Untuk urutan umum, tidak ada cara untuk mengulang secara terbalik - urutannya bisa tak terbatas, misalnya:
Apa yang Anda harapkan terjadi jika Anda mencoba mengulanginya secara terbalik?
sumber
Sebelum menggunakan
foreach
untuk iterasi, balikkan daftar denganreverse
metode:sumber
myList
yang akan membantu. IEnumerable.Reverse tidak bekerja di sini.myList
tampaknya bertipeSystem.Collections.Generic.List<System.Windows.Documents.List>
(atauList
jenis kustom lainnya ) kalau tidak, kode ini tidak berfungsi: PKadang-kadang Anda tidak memiliki kemewahan pengindeksan, atau mungkin Anda ingin membalikkan hasil kueri Linq, atau mungkin Anda tidak ingin mengubah kumpulan sumber, jika ada yang benar, Linq dapat membantu Anda.
Metode ekstensi Linq menggunakan tipe anonim dengan Linq Select untuk memberikan kunci penyortiran untuk Linq OrderByDescending;
Pemakaian:
Itu bernama "Balikkan" karena ini identik dengan "Balikkan" dan memungkinkan disambiguasi dengan implementasi Daftar Balikkan.
Dimungkinkan untuk membalikkan rentang koleksi tertentu juga, karena Int32.MinValue dan Int32.MaxValue berada di luar kisaran jenis indeks pengumpulan apa pun, kami dapat memanfaatkannya untuk proses pemesanan; jika indeks elemen di bawah rentang yang diberikan, itu diberikan Int32.MaxValue sehingga pesanannya tidak berubah saat menggunakan OrderByDescending, sama halnya, elemen pada indeks yang lebih besar dari rentang yang diberikan, akan diberikan Int32.MinValue, sehingga mereka muncul di akhir proses pemesanan. Semua elemen dalam rentang yang diberikan diberi indeks normal dan dibalik sesuai.
Pemakaian:
Saya tidak yakin dengan kinerja hit implementasi Linq ini versus menggunakan Daftar sementara untuk membungkus koleksi untuk dibalik.
Pada saat penulisan, saya tidak mengetahui implementasi Reverse Linq sendiri, tetap saja, ini sangat menyenangkan. https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx
sumber
Jika Anda menggunakan Daftar <T>, Anda juga dapat menggunakan kode ini:
Ini adalah metode yang menulis daftar terbalik dengan sendirinya.
Sekarang kedepan:
Outputnya adalah:
sumber
Hal ini mungkin jika Anda dapat mengubah kode koleksi yang menerapkan IEnumerable atau IEnumerable (misalnya implementasi sendiri dari IList).
Buat Iterator melakukan pekerjaan ini untuk Anda, misalnya seperti implementasi berikut melalui antarmuka IEnumerable (dengan asumsi 'item' adalah bidang Daftar dalam sampel ini):
Karena ini Daftar Anda akan diulang dalam urutan terbalik melalui daftar Anda.
Sekedar petunjuk: Anda harus dengan jelas menyatakan perilaku khusus dari daftar ini di dalam dokumentasi (bahkan lebih baik dengan memilih nama kelas yang menjelaskan sendiri seperti Stack atau Queue, juga).
sumber
Tidak. ForEach hanya mengulang melalui pengumpulan untuk setiap item dan pesanan tergantung apakah ia menggunakan IEnumerable atau GetEnumerator ().
sumber
Menguraikan sedikit tentang jawaban yang bagus dari Jon Skeet , ini bisa serbaguna:
Dan kemudian gunakan sebagai
sumber
Saya telah menggunakan kode ini yang berfungsi
sumber
.Reverse()
sebenarnya memodifikasi daftar.Ini bekerja dengan cukup baik
sumber