Человек спросит, как с помощью Linq проверить упорядоченность массива, к примеру {1, 3, 5, 7, 9}.
Варианты ответа:
1.
var s = new[] {1, 3, 3, 7, 9};
var r = s.Aggregate(new {V=int.MinValue, C=0}, (a, val) => a.V < val ? new {V=val, C=a.C+1} : a).C == s.Length;
2.
var ascending = Enumerate()
.Memoize(1)
.Let(e => e.Zip(e.Skip(1), (a, b) => a < b))
.All(x => x);
(это с применением Rx, кстати кто скажет что это такое?)
3.
var isAscending = xs.OrderBy(x => x).SequenceEqual(xs);
4.
var isAscending = xs.Replay(ys => ys.Zip(ys.Skip(1), (x, y) => x <= y), 1).All(x => x);
Мне, как поверхностно знакомому с Linq (самые простые конструкции я использую) понятен только вариант 3 (там сначала сортировка всего массива -- это очень необдуманно, сортировка не нужна в данном случае). Остальное мне нужно сидеть минут 5 и думать что же хотел сказать этот индус.
Мой вариант (долго не думал):
var numbers = new[] {1, 3, 5, 7, 9};
bool isAscending = true;
int temp = numbers[0];
for (int i = 1; i < numbers.Length; i++)
{
if (temp > numbers[i])
{
isAscending = false;
break;
}
temp = numbers[i];
}
Теперь скажите чей вариант быстрее и понятнее? Каков смысл в вашем Linq?
Re: Помогает ли Linq сделать код понятнее или быстрее?
0K>Человек спросит, как с помощью Linq проверить упорядоченность массива, к примеру {1, 3, 5, 7, 9}.
Прочитал исходное обсуждение, увидел:
Помня одно предыдущее значение, соедини последовательность с самой собой, смещённой с на один элемент вперёд, и проверь, что во всех парах левое значение меньше или равно правому.
Это всё очень красиво. Да-да, красиво, так говорит моё программерское чувство прекрасного. Но оно так же и бесполезно. Более того, оно вредно, потому что насмотрятся нубы на такое и будут делать всегда и везде, не думая ни о скорости, ни о выделении памяти, ни о побочных эффектах. А это печально.
Курица — это инструмент, с помощью которого одно яйцо производит другие.
Re[2]: Помогает ли Linq сделать код понятнее или быстрее?
Здравствуйте, frogkiller, Вы писали:
F>... и будут делать всегда и везде, не думая ни о скорости, ни о выделении памяти, ни о побочных эффектах. А это печально.
Печальнее всего еще и то, что в 9 случаях из 10 оно "красиво-нечитабельное". Попробуй потом такой код "поддержать"...
ЗЫ: задачи на логику на собеседованиях — зло! шибко "умный" программер придет — точно "умничать" будет.
Голь на выдумку хитра, однако...
Re: Помогает ли Linq сделать код понятнее или быстрее?
Во-первых, у тебя оформление кода неправильное. Приближаться к "мирку" (к декларативному другому "мирку"), думаю, надо начать с оформления кода. Все надо стараться записать одной строчкой, переменным давать имена из одного-двух символов, никогда не указывать имена типов, главное правило: всегда плевать на эффективность ради краткости, ну и другие полезные практики:
var e = true; for (var i = 1; i < xs.Length; i++) if (xs[i-1] > xs[i]) e = false;
var isAscending = e;
Во, вот так к "мирку" уже ближе. Декларативнее. Приобщайся!
Собрался ставить минус? Да сам иди в жопу!
.
Re: Помогает ли Linq сделать код понятнее или быстрее?
Здравствуйте, 0K, Вы писали:
0K>Теперь скажите чей вариант быстрее и понятнее? Каков смысл в вашем Linq?
Твой в любом случае хуже всех, так как принимает массив, а не любую последовательность. Твой вариант является наименее общим при том что самый длинный.
В F# есть потрясающая функция Seq.pairwise, которая список [x1,x2,x3...] превращает в список пар [(x1,x2), (x2,x3)...] делает примерно тоже что в вариантах 2 и 4, только с одним итератором и совершенно не требует эмуляции ленивости с запоминанием.
Написать аналогичную функцию для C# несложно и при её использовании проверка на упорядоченность последовательности будет короткой, красивой и даже очевидной. При этом можно будет проверять на упорядоченность последовательность чисел, принимаемых по сети\доставаемых из бд, без сохранения всей последовательности в памяти.
Кроме того алгоритмы с Linq можно композировать, например для получения упорядоченных подпоследовательностей.
Re: Помогает ли Linq сделать код понятнее или быстрее?
Здравствуйте, SpaceConscience, Вы писали:
SC>Во-первых, у тебя оформление кода неправильное. Приближаться к "мирку" (к декларативному другому "мирку"), думаю, надо начать с оформления кода. Все надо стараться записать одной строчкой, переменным давать имена из одного-двух символов, никогда не указывать имена типов, главное правило: всегда плевать на эффективность ради краткости, ну и другие полезные практики:
SC>
SC>var e = true; for (var i = 1; i < xs.Length; i++) if (xs[i-1] > xs[i]) e = false;
SC>var isAscending = e;
SC>
Много недостатков алгоритм даже не квадратичный, "if" вместо оператора "?" мутабельный e нет рекурсии, в общем незачет
Re[2]: Помогает ли Linq сделать код понятнее или быстрее?
Здравствуйте, gandjustas, Вы писали:
0K>>Теперь скажите чей вариант быстрее и понятнее? Каков смысл в вашем Linq? G>Твой в любом случае хуже всех, так как принимает массив, а не любую последовательность. Твой вариант является наименее общим при том что самый длинный.
его вариант на самом деле лучший, самый понятный и самых быстрых (только проверку на пустой массив надо приделать).
стремление к общности там где она нафиг не нужна — большая ошибка на самом деле.
G>В F# есть потрясающая функция Seq.pairwise, которая список [x1,x2,x3...] превращает в список пар [(x1,x2), (x2,x3)...] делает примерно тоже что в вариантах 2 и 4, только с одним итератором и совершенно не требует эмуляции ленивости с запоминанием.
работает, правда, все в разы медленнее, но кого нынче это волнует...
Re[3]: Помогает ли Linq сделать код понятнее или быстрее?
Здравствуйте, vitasR, Вы писали:
R>его вариант на самом деле лучший, самый понятный и самых быстрых (только проверку на пустой массив надо приделать).
Самый понятный по моему рекурсивный, вот вариант на близком к шарпу D
pure auto f(int Arr[])
{
if(Arr.length < 2)
return true;
if(Arr[0] > Arr[1])
return false;
return f(Arr[1 .. $]);
}
Тут сразу видна суть в императивном же варианте временная переменная ее заслоняет.
Ну и по эффективности из-за оптимизации хвостовой рекурсии этот вариант практически эквивалентен циклу:
if(Arr.length < 2)
00402017 push ebx
00402018 push esi
00402019 mov esi,dword ptr [esp+2Ch]
0040201D cmp esi,2
00402020 push edi
00402021 jae main@f+21h (402031h)
return true;
00402023 pop edi
00402024 mov eax,1
00402029 pop esi
0040202A pop ebx
0040202B add esp,20h
0040202E ret 8
if(Arr[0] > Arr[1])
00402031 mov edx,ecx
00402033 mov ebx,dword ptr [edx]
00402035 mov eax,esi
00402037 cmp ebx,dword ptr [edx+4]
0040203A jle main@f+37h (402047h)
return false;
0040203C pop edi
0040203D xor eax,eax
0040203F pop esi
00402040 pop ebx
00402041 add esp,20h
00402044 ret 8
return f(Arr[1 .. $]);
00402047 lea edi,[esi-1]
0040204A add edx,4
0040204D mov ecx,edx
0040204F mov dword ptr [esp+0Ch],edi
00402053 mov esi,edi
00402055 mov dword ptr [esp+10h],edx
00402059 cmp dword ptr [esp+0Ch],2
0040205E jb main@f+13h (402023h)
00402060 jmp main@f+21h (402031h)
Re[3]: Помогает ли Linq сделать код понятнее или быстрее?
Здравствуйте, vitasR, Вы писали:
R>Здравствуйте, gandjustas, Вы писали:
0K>>>Теперь скажите чей вариант быстрее и понятнее? Каков смысл в вашем Linq? G>>Твой в любом случае хуже всех, так как принимает массив, а не любую последовательность. Твой вариант является наименее общим при том что самый длинный.
R>его вариант на самом деле лучший, самый понятный и самых быстрых (только проверку на пустой массив надо приделать).
Скажи по-русски что такое отсортированный массив.
R>стремление к общности там где она нафиг не нужна — большая ошибка на самом деле.
Обоснуй.
G>>В F# есть потрясающая функция Seq.pairwise, которая список [x1,x2,x3...] превращает в список пар [(x1,x2), (x2,x3)...] делает примерно тоже что в вариантах 2 и 4, только с одним итератором и совершенно не требует эмуляции ленивости с запоминанием.
R>работает, правда, все в разы медленнее, но кого нынче это волнует...
Конечно, не волнует. Время компьютера дешево, а время программиста дорого.
Re[2]: Помогает ли Linq сделать код понятнее или быстрее?
Здравствуйте, gandjustas, Вы писали:
G>Твой в любом случае хуже всех, так как принимает массив, а не любую последовательность.
Для тех, кто не уловил принцип:
bool isOrdered = true;
enumerator.MoveNext();
var temp = enumerator.Current;
while (enumerator.MoveNext())
{
if (temp > enumerator.Current)
{
isOrdered = false;
break;
}
temp = enumerator.Current;
}
Разницы нет последовательность или тип, который нужно листать с помощью вызова функции. Вообще в 99% массивы предпочтительнее -- все равно все хранится в памяти.
G>Твой вариант является наименее общим при том что самый длинный.
Да? А так:
bool o = true; e.MoveNext();
var temp = e.Current;
while (e.MoveNext()){ if (temp > e.Current) { o = false; break; } temp = e.Current; }
Мальчик. Объясняю пока жив: главное понятность кода а не во сколько байт ты его втиснул.
Re[2]: Помогает ли Linq сделать код понятнее или быстрее?
Здравствуйте, IT, Вы писали:
IT>Хотя бы в том, что твой пример без Linq упадёт на пустой последовательности.
А ваш пример на Linq для пустой последовательности выдаст true или false? Значение для пустой последовательности не определено -- это зависит от логики программы. По этому проверку я не включал.
Re[3]: Помогает ли Linq сделать код понятнее или быстрее?
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>Твой в любом случае хуже всех, так как принимает массив, а не любую последовательность.
0K>Разницы нет последовательность или тип, который нужно листать с помощью вызова функции. Вообще в 99% массивы предпочтительнее -- все равно все хранится в памяти.
Это ты где такую шутку прочитал?
99% данных обычно программа получает извне.
Re[4]: Помогает ли Linq сделать код понятнее или быстрее?
Здравствуйте, 0K, Вы писали:
0K>главное понятность кода а не во сколько байт ты его втиснул.
Понятность понятие субъективное.
Декларативный код обычно выигрывает в понятности, особенно если для этого есть примитивы, в Linq их правда похоже нет.
Re[4]: Помогает ли Linq сделать код понятнее или быстрее?
Здравствуйте, 0K, Вы писали:
0K>Кстати, философский вопрос. При передаче пустого массива нужно вернуть true или false?
Если взять любой упорядоченный массив, то очевидно что все массивы, получающиеся из исходного вычеркиванием отдельных элементов, также будут упорядоченными. Значит и пустой массив обязан быть таковым
Re[5]: Помогает ли Linq сделать код понятнее или быстрее?
Здравствуйте, FR, Вы писали:
0K>>Кстати, философский вопрос. При передаче пустого массива нужно вернуть true или false?
FR>true, также и для массива из одного элемента.
Для массива из одного элемента -- согласен. А если вообще элементов нет?
Re[4]: Помогает ли Linq сделать код понятнее или быстрее?