Enumerable.ZipLinq'te uzatma yönteminin kullanımı nedir ?
Enumerable.ZipLinq'te uzatma yönteminin kullanımı nedir ?
Yanıtlar:
Zip operatörü, belirtilen bir seçici işlevini kullanarak iki dizinin karşılık gelen öğelerini birleştirir.
var letters= new string[] { "A", "B", "C", "D", "E" };
var numbers= new int[] { 1, 2, 3 };
var q = letters.Zip(numbers, (l, n) => l + n.ToString());
foreach (var s in q)
Console.WriteLine(s);
Çıkışı
A1
B2
C3
Zipalternatifinizi yazın . B) bir yöntem yazın yield returndaha kısa listenin her bir elemanına ve ardından devam yield returning defaultsüresiz bundan sonra. (B Seçeneği, hangi listenin daha kısa olduğunu önceden bilmenizi gerektirir.)
Zipiki diziyi tek bir dizide birleştirmek içindir. Örneğin, dizileriniz varsa
1, 2, 3
ve
10, 20, 30
ve her dizide aynı konumdaki öğelerin çarpılmasının sonucu olan dizinin
10, 40, 90
söyleyebilirdin
var left = new[] { 1, 2, 3 };
var right = new[] { 10, 20, 30 };
var products = left.Zip(right, (m, n) => m * n);
Bir sırayı fermuarın sol tarafı olarak ve diğer sıralamayı fermuarın sağ tarafı olarak düşündüğünüz için "fermuar" olarak adlandırılır ve fermuar operatörü dişleri eşleştirerek iki tarafı birlikte çeker ( dizinin öğeleri) uygun şekilde.
İki diziyi yineler ve öğelerini tek tek tek bir yeni dizide birleştirir. Yani, A dizisindeki bir elemanı alıp, onu B dizisindeki karşılık gelen elemanla dönüştürürsünüz ve sonuç C dizisinin bir elemanını oluşturur.
Bunu düşünmenin bir yolu Select, aynı koleksiyondaki öğeleri tek bir koleksiyondan dönüştürmek yerine, aynı anda iki koleksiyon üzerinde çalışması dışında benzer olmasıdır .
Gönderen yöntemine MSDN makalesinde :
int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };
var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second);
foreach (var item in numbersAndWords)
Console.WriteLine(item);
// This code produces the following output:
// 1 one
// 2 two
// 3 three
Bunu emir kodu ile yapacak olsaydın, muhtemelen şöyle bir şey yapardın:
for (int i = 0; i < numbers.Length && i < words.Length; i++)
{
numbersAndWords.Add(numbers[i] + " " + words[i]);
}
Veya LINQ yoksa Zip, bunu yapabilirdiniz:
var numbersAndWords = numbers.Select(
(num, i) => num + " " + words[i]
);
Bu, her biri aynı uzunluk ve sıraya sahip ve her biri aynı nesne kümesinin farklı bir özelliğini açıklayan basit, dizi benzeri listelere yayılmış verileriniz olduğunda kullanışlıdır. Zipbu veri parçalarını daha tutarlı bir yapıya dönüştürmenize yardımcı olur.
Dolayısıyla, bir dizi durum adınız ve bunların başka bir kısaltmaları dizisine sahipseniz, bunları aşağıdaki Stategibi bir sınıfta harmanlayabilirsiniz :
IEnumerable<State> GetListOfStates(string[] stateNames, int[] statePopulations)
{
return stateNames.Zip(statePopulations,
(name, population) => new State()
{
Name = name,
Population = population
});
}
Select
İsmin Zipsizi atmasına izin VERMEYİN . Bir dosyayı veya klasörü sıkıştırırken (sıkıştırma) olduğu gibi sıkıştırmayla ilgisi yoktur. Aslında ismini giysilerin üzerindeki bir fermuarın çalışma şeklinden alıyor: Giysilerin üzerindeki fermuarın 2 tarafı ve her iki tarafında da bir demet diş var. Tek yöne gittiğinizde fermuar her iki tarafı numaralandırır (hareket eder) ve dişleri sıkarak fermuarı kapatır. Diğer yöne gittiğinizde dişleri açar. Ya açık ya da kapalı bir fermuarla bitirirsiniz.
ZipYöntemle aynı fikirdir . İki koleksiyonumuzun olduğu bir örnek düşünün. Biri harfleri, diğeri de o harfle başlayan bir gıda maddesinin adını tutar. Açıklık amacıyla onları arıyorum leftSideOfZipperve rightSideOfZipper. İşte kod.
var leftSideOfZipper = new List<string> { "A", "B", "C", "D", "E" };
var rightSideOfZipper = new List<string> { "Apple", "Banana", "Coconut", "Donut" };
Görevimiz, meyvenin harfini a :ve adı ile ayrılmış bir koleksiyon oluşturmaktır . Bunun gibi:
A : Apple
B : Banana
C : Coconut
D : Donut
Zipkurtarmak için. Fermuar terminolojimize ayak uydurmak için bu sonuca closedZipperve arayacağımız sol fermuarın eşyalarına leftTooth, sağ tarafa da righToothbariz nedenlerle arayacağız :
var closedZipper = leftSideOfZipper
.Zip(rightSideOfZipper, (leftTooth, rightTooth) => leftTooth + " : " + rightTooth).ToList();
Yukarıda fermuarın sol tarafını ve fermuarın sağ tarafını sıralıyoruz (seyahat ediyor) ve her dişe bir işlem yapıyoruz. Yaptığımız operasyon sol dişi (yemek harfi) önce a :ve ardından sağ diş (yemek adı) ile birleştirmektir. Bunu şu kodu kullanarak yapıyoruz:
(leftTooth, rightTooth) => leftTooth + " : " + rightTooth)
Sonuç şudur:
A : Apple
B : Banana
C : Coconut
D : Donut
Son E harfine ne oldu?
Gerçek bir elbise fermuarını sayıyorsanız (çekiyorsanız) ve bir tarafı, sol tarafı veya sağ tarafı önemli değil, diğer tarafa göre daha az dişe sahipse ne olur? Peki fermuar orada duracak. ZipYöntem tamamen aynı olacaktır: Duracak o iki tarafında son öğeyi ulaştığında. Bizim durumumuzda, sağ tarafta daha az diş (yiyecek isimleri) vardır, bu yüzden "Donut" da duracaktır.
Yorumlar bölümünde yayınlayacak temsilci puanlarım yok, ancak ilgili soruyu cevaplamak için:
Bir listenin öğeleri kalmadığında zip'in devam etmesini istersem ne olur? Bu durumda, daha kısa liste öğesi varsayılan değeri almalıdır. Bu durumda çıktı A1, B2, C3, D0, E0 olacaktır. - liang 19 Kasım 2015, 3:29
Yapacağınız şey, daha kısa diziyi varsayılan değerlerle doldurmak için Array.Resize () kullanmak ve ardından bunları birlikte Zip () kullanmaktır.
Kod örneği:
var letters = new string[] { "A", "B", "C", "D", "E" };
var numbers = new int[] { 1, 2, 3 };
if (numbers.Length < letters.Length)
Array.Resize(ref numbers, letters.Length);
var q = letters.Zip(numbers, (l, n) => l + n.ToString());
foreach (var s in q)
Console.WriteLine(s);
Çıktı:
A1
B2
C3
D0
E0
Array.Resize () kullanmanın bir uyarı olduğunu lütfen unutmayın : C # içinde Redim Preserve?
Hangi dizinin daha kısa olacağı bilinmiyorsa, onu destekleyen bir işlev oluşturulabilir:
static void Main(string[] args)
{
var letters = new string[] { "A", "B", "C", "D", "E" };
var numbers = new int[] { 1, 2, 3 };
var q = letters.Zip(numbers, (l, n) => l + n.ToString()).ToArray();
var qDef = ZipDefault(letters, numbers);
Array.Resize(ref q, qDef.Count());
// Note: using a second .Zip() to show the results side-by-side
foreach (var s in q.Zip(qDef, (a, b) => string.Format("{0, 2} {1, 2}", a, b)))
Console.WriteLine(s);
}
static IEnumerable<string> ZipDefault(string[] letters, int[] numbers)
{
switch (letters.Length.CompareTo(numbers.Length))
{
case -1: Array.Resize(ref letters, numbers.Length); break;
case 0: goto default;
case 1: Array.Resize(ref numbers, letters.Length); break;
default: break;
}
return letters.Zip(numbers, (l, n) => l + n.ToString());
}
ZipDefault () ile birlikte düz .Zip () çıktısı:
A1 A1
B2 B2
C3 C3
D0
E0
Orijinal sorunun ana cevabına geri dönecek olursak, yapmak isteyebileceğiniz bir başka ilginç şey ("sıkıştırılacak" dizilerin uzunlukları farklı olduğunda), listenin sonuna gelecek şekilde onları birleştirmektir. üst yerine eşleşir. Bu, .Skip () kullanılarak uygun sayıda öğe "atlanarak" gerçekleştirilebilir.
foreach (var s in letters.Skip(letters.Length - numbers.Length).Zip(numbers, (l, n) => l + n.ToString()).ToArray())
Console.WriteLine(s);
Çıktı:
C1
D2
E3
public static IEnumerable<T> Pad<T>(this IEnumerable<T> input, long minLength, T value = default(T)) { long numYielded = 0; foreach (T element in input) { yield return element; ++numYielded; } while (numYielded < minLength) { yield return value; ++numYielded; } }
Buradaki cevapların çoğu gösteriyor Zip, ancak kullanımını motive edecek gerçek bir kullanım durumunu gerçekten açıklamadan Zip.
ZipBirbirini izleyen şey çiftlerini yinelemek için harika olan özellikle yaygın bir model . Bu bir enumerator yineleme yapılır X1 elemanının atlayarak, kendisi ile: x.Zip(x.Skip(1). Görsel Örnek:
x | x.Skip(1) | x.Zip(x.Skip(1), ...)
---+-----------+----------------------
| 1 |
1 | 2 | (1, 2)
2 | 3 | (2, 1)
3 | 4 | (3, 2)
4 | 5 | (4, 3)
Bu ardışık çiftler, değerler arasındaki ilk farkları bulmak için kullanışlıdır. Örneğin, ardışık çiftler IEnumable<MouseXPosition>üretmek için kullanılabilir IEnumerable<MouseXDelta>. Benzer şekilde, a'nın örneklenmiş booldeğerleri / / / buttongibi olaylara yorumlanabilir . Bu olaylar, daha sonra yöntemlerin temsilciliğine yönelik çağrıları yönlendirebilir. İşte bir örnek:NotPressedClickedHeldReleased
using System;
using System.Collections.Generic;
using System.Linq;
enum MouseEvent { NotPressed, Clicked, Held, Released }
public class Program {
public static void Main() {
// Example: Sampling the boolean state of a mouse button
List<bool> mouseStates = new List<bool> { false, false, false, false, true, true, true, false, true, false, false, true };
mouseStates.Zip(mouseStates.Skip(1), (oldMouseState, newMouseState) => {
if (oldMouseState) {
if (newMouseState) return MouseEvent.Held;
else return MouseEvent.Released;
} else {
if (newMouseState) return MouseEvent.Clicked;
else return MouseEvent.NotPressed;
}
})
.ToList()
.ForEach(mouseEvent => Console.WriteLine(mouseEvent) );
}
}
Baskılar:
NotPressesd
NotPressesd
NotPressesd
Clicked
Held
Held
Released
Clicked
Released
NotPressesd
Clicked
Diğerlerinin de belirttiği gibi Zip, iki koleksiyonu birleştirip daha fazla Linq ifadesinde veya bir foreach döngüsünde kullanmanıza izin verir.
Bir for döngüsü ve iki dizi gerektiren işlemler artık anonim bir nesne kullanılarak bir foreach döngüsünde yapılabilir.
Az önce keşfettiğim bir örnek, bu biraz saçma, ancak paralelleştirme yararlı olsaydı yararlı olabilirdi, yan etkileri olan tek bir kuyruk geçişi olurdu:
timeSegments
.Zip(timeSegments.Skip(1), (Current, Next) => new {Current, Next})
.Where(zip => zip.Current.EndTime > zip.Next.StartTime)
.AsParallel()
.ForAll(zip => zip.Current.EndTime = zip.Next.StartTime);
timeSegments, bir kuyruktaki geçerli veya kuyruğu kaldırılmış öğeleri temsil eder (son öğe Zip tarafından kesilir). timeSegments.Skip (1), bir kuyruktaki sonraki veya gözetleme öğelerini temsil eder. Zip yöntemi, bu ikisini Next ve Current özelliğine sahip tek bir anonim nesnede birleştirir. Sonra Where ile filtreliyoruz ve AsParallel (). ForAll ile değişiklik yapıyoruz. Elbette son bit, normal bir foreach veya rahatsız edici zaman dilimlerini döndüren başka bir Select ifadesi olabilir.
Zip yöntemi, sizin tarafınızdan, yani arayan tarafından bir birleştirme işlevi sağlayıcısı kullanarak iki ilgisiz diziyi "birleştirmenize" olanak tanır. MSDN'deki örnek, Zip ile neler yapabileceğinizi göstermede oldukça iyidir. Bu örnekte, iki rastgele, ilgisiz dizi alır ve bunları rastgele bir işlev kullanarak birleştirirsiniz (bu durumda, her iki dizideki öğeleri tek bir dizede birleştirerek).
int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };
var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second);
foreach (var item in numbersAndWords)
Console.WriteLine(item);
// This code produces the following output:
// 1 one
// 2 two
// 3 three
string[] fname = { "mark", "john", "joseph" };
string[] lname = { "castro", "cruz", "lopez" };
var fullName = fname.Zip(lname, (f, l) => f + " " + l);
foreach (var item in fullName)
{
Console.WriteLine(item);
}
// The output are
//mark castro..etc