Есть две последовательности одинаковой длины. Как для них построить соединение, которое бы соотносило элементы с одинаковыми индексами?
То есть на входе { 1, 2, 3 }, { "ein", "zwei", "drei" }. А на выходе { { 1, "ein" }, { 2, "zwei" }, { 3, "drei" } }.
Интересует человеческий вариант на LINQ-операторах, а не это убожество, приведенное ниже.
var digits = new[] { 1, 2, 3 };
var german = new[] { "ein", "zwei", "drei" };
var q = digits.Select( ( digit, index ) => new { Value = digit, Index = index } )
.Join( german.Select( ( phrase, index ) => new { Value = phrase, Index = index } ),
_ => _.Index, _ => _.Index,// Вот это очень плохо читается; с первого взгляда не очевидно, что именно тут индексы на равенство и сравниваются.
( digit, phrase ) => new { Digit = digit.Value, Phrase = phrase.Value }
);
foreach ( var translation in q )
Console.WriteLine( "'{0}' stands for {1}", translation.Phrase, translation.Digit );
Здравствуйте, SergASh, Вы писали:
SAS>Привет всем!
SAS>Есть две последовательности одинаковой длины. Как для них построить соединение, которое бы соотносило элементы с одинаковыми индексами?
SAS>То есть на входе { 1, 2, 3 }, { "ein", "zwei", "drei" }. А на выходе { { 1, "ein" }, { 2, "zwei" }, { 3, "drei" } }.
SAS>Интересует человеческий вариант на LINQ-операторах, а не это убожество, приведенное ниже. SAS>Спасибо.
Думаю проще будет написать свой Extension-метод Merge. Кстати на форуме где-то такое пробегало.
Здравствуйте, SergASh, Вы писали:
SAS>Привет всем!
SAS>Есть две последовательности одинаковой длины. Как для них построить соединение, которое бы соотносило элементы с одинаковыми индексами?
SAS>То есть на входе { 1, 2, 3 }, { "ein", "zwei", "drei" }. А на выходе { { 1, "ein" }, { 2, "zwei" }, { 3, "drei" } }.
SAS>Интересует человеческий вариант на LINQ-операторах, а не это убожество, приведенное ниже.
... SAS>Спасибо.
Ну сходу приходит на ум вот это. Но сразу оговорюсь медленно хотя, с другой стороны, понятно и скорость написания высокая
int[] digits = { 1, 2, 3 };
string[] german = new[] { "ein", "zwei", "drei" };
var result = from digit in digits
join phrase in german on digits.ToList<int>().IndexOf(digit) equals german.ToList<string>().IndexOf(phrase)
select new { Digit = digit, Phrase = phrase};
foreach ( var translation in result )
Console.WriteLine( "'{0}' stands for {1}", translation.Phrase, translation.Digit );
D> using (var e1 = first.GetEnumerator())
D> using (var e2 = second.GetEnumerator())
D> while (e1.MoveNext() && e2.MoveNext())
D> yield return func(e1.Current, e2.Current);
D> }
D>}
D>
Насколько корректно позволять склеивать последовательности разной длинны?
Может быть было бы корректнее допускать склеивании только последовательностей имеющих одинаковую длину?
Вопрос не праздный, так как давно хотел добавить аналогичную функцию в библиотеку Немерла.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>Насколько корректно позволять склеивать последовательности разной длинны? VD>Может быть было бы корректнее допускать склеивании только последовательностей имеющих одинаковую длину?
VD>Вопрос не праздный, так как давно хотел добавить аналогичную функцию в библиотеку Немерла.
Например, Seq.zip из стандартной библиотеки F# игнорирует остаток последовательности большей длины.
Zip из Haskell ведет себя также:
If one input list is short, excess elements of the longer list are discarded.
Здравствуйте, desco, Вы писали:
D>можно, например, так:
D>
D> public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
D> this IEnumerable<TFirst> first,
D> IEnumerable<TSecond> second,
D> Func<TFirst, TSecond, TResult> func)
D>
Кстати, такая функция должна называться не Zip, а Map/Map2/Select/Select2, так как он позволяет произвольное отображение. А Zip на шарпе можно создать только с использованием типа наподобии System.Collections.Generic.KeyValuePair, так как Zip должен возвращать список кортежей, а оных в C# нет. На роль кортежей могли бы пойти анонимные типы, но их нельзя описать, а стало быть и использовать в качестве возвращаемых значений функций.
public static IEnumerable<KeyValuePair<TFirst, TSecond>> Zip<TFirst, TSecond>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second)
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, desco, Вы писали:
D>Например, Seq.zip из стандартной библиотеки F# игнорирует остаток последовательности большей длины. D>Zip из Haskell ведет себя также: D>
D>If one input list is short, excess elements of the longer list are discarded.
Вот только хорошо ли это? Насколько часто встречаются случаи когда разница в длине не важна?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Детский вопрос по LINQ
От:
Аноним
Дата:
05.10.08 01:56
Оценка:
Здравствуйте, desco, Вы писали:
D>Например, Seq.zip из стандартной библиотеки F# игнорирует остаток последовательности большей длины. D>Zip из Haskell
Хаскель оперирует только ленивыми последовательностями, F# — как ленивыми (Seq), так и строгими (List и Array)
Для ленивых, как мне кажется, общим паттерном является "игнорировать несоответствие", для строгих — "кидать исключение".
LINQ — ленив (LINQ-овский Take, например, соответствует "ленивой парадигме" — возвращает "все что есть", а не кидает исключением если запрошено больше элементов чем имеется), так то данная имплементация (которая кстати соответствует хаскельному TakeWith) мне видится абсолютно верной.
To VladD2: Насчет "как часто приходтся зиповать последовательности разной длины" — да постоянно. Чаще как раз приходится зиповать разной длины (особенно если участвуют бесконечные списки):
fib :: Integer -> Integer
fib n = fibs !! n
where
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
Т.е. зачастую до конца какого-либо списка дело просто и не доходит:
Здравствуйте, SergASh, Вы писали:
SAS>Есть две последовательности одинаковой длины. Как для них построить соединение, которое бы соотносило элементы с одинаковыми индексами?
SAS>То есть на входе { 1, 2, 3 }, { "ein", "zwei", "drei" }. А на выходе { { 1, "ein" }, { 2, "zwei" }, { 3, "drei" } }.
SAS>Интересует человеческий вариант на LINQ-операторах...
А вот так в рамках Ваших условий можно?
int[] digits ={ 1, 2, 3 };
string[] german = { "ein", "zwei", "drei" };
// вариант 1var c = digits.Select((digit, index) => new { Digit = digit, Phrase = german[index] });
// вариант 2, медленныйvar d = digits.Select((digit, index) => new { Digit = digit, Phrase = german.Skip(index).First() });
foreach (var e in c)
{
Console.WriteLine("" + e.Digit + " " + e.Phrase);
}
Согласен, Zip/Select2 вполне решают задачу, но изначально я хотел узнать есть ли способ получить индекс элемента последовательности используя только LINQ-операторы, а не extension-методы. Похоже что нет
Здравствуйте, SergASh, Вы писали:
SAS>Здравствуйте, adanov, Вы писали:
A>>А вот так в рамках Ваших условий можно? ... SAS>Не-а. Это был наколенный пример, в реале на входе идут IEnumerable<...>
Тогда вариант №3:
static void Main(string[] args)
{
var c = from a in digits(10).Select((e, i) => new { e, i })
join b in german(10).Select((e, i) => new { e, i })
on a.i equals b.i
select new { d = a.e, f = b.e };
foreach (var e in c)
{
Console.WriteLine("" + e.d + " " + e.f);
}
}
static IEnumerable<int> digits(int n)
{
for (int i = 1; i <= n; i++)
yield return i;
}
static IEnumerable<string> german(int n)
{
for (int i = 1; i <= n; i++)
yield return string.Format("{0:D2}", i);
}
Итоговая сложность алгоритма O(N) для n < 10000, затем негативоно сказывается нехватка кэша.