Здравствуйте, igna, Вы писали:
I>Здравствуйте, samius, Вы писали:
S>>И что же даст знание о том что специфичные методы не используются для локальной переменной?
I>Например понимание того, что можно заменить FileStream на какой-либо другой Stream без модификации дальнейшего кода.
Если нужно такое понимание, то следует сделать Extract Method с параметром типа Stream.
Здравствуйте, AndrewVK, Вы писали:
AVK>Если проблем нет, то, с большой вероятностью, это означает что ты их пока не видишь
Большей частью я решаю проблемы по мере их поступления. Потому как если пофантазировать десять минут по поводу того, какие проблемы могут возникнуть, и как их предотвратить, то надо будет удавиться.
Конечно, на кое-что закладываешь еще в самом начале, не надо понимать все утрировано
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Сформулируйте, когда LinQ стоит применять и когда его применение нецелесообразно.
Linq to XML — стоит применять почти всегда, так как это, на мой взгляд, наиболее удобная объектная модель для работы с XML.
Linq to objects — применять в тех случаях, когда улучшается читаемость кода, то есть в подавляющем большинстве. Противопоказания такие же как и при применении цикла foreach — если надо выжимать такты, то не использовать.
Linq to SQL — ничего сказать не могу, так как не работаю постоянно с БД.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Сформулируйте, когда LinQ стоит применять и когда его применение нецелесообразно.
Во-первых, следует уточнить о котором из LINQ-ов речь. Ответить за целесообразность применения всех провайдеров зараз нельзя.
Отвечу за LINQ 2 Objects.
Вообще это высокоурвневый инструмент и к нему применимы обычные соображения применения высокоуровневых инструментов. Т.е. каждый решает за себя в контексте целей. Например, проверка числа на простоту средствами LINQ-а будет весьма наглядна и декларативна, но это будет не лучшее решение в аспекте производительности. Применять ли в данном случае LINQ — следует решать исходя из целей. Если стоит цель написать легкоподдерживаемый и наглядный код, то да, LINQ целесообразен. Если стоит цель выжать производительность до такта, то ответ — нет.
(по поводу целесообразности можно провести аналогию с другим высокоуровневым инструментом — двоичной сериализацией. Да, сериализация действительно медленная и не такая компактная как хотелось бы и иногда имеет смысл от нее отказываться, но .NET Remoting без нее бы не состоялся, либо был бы сплошным геморроем. Без сериализации целый класс приложений был бы значительно более трудоемким в разработке)
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Сформулируйте, когда LinQ стоит применять и когда его применение нецелесообразно.
По поводу Linq to XML и Linq to objects предыдущие ораторы очень хорошо сказали, мне добавить нечего.
Что касается Linq to Sql — мой опыт общения с ним говорит, что его можно применять в небольших приложениях.
При этом ни в коем случае не внедрять бизнес-логику в сгенерированные классы через механизм частичных классов, хотя так кое-где советуют.
Здравствуйте, samius, Вы писали:
S>Вообще это высокоурвневый инструмент и к нему применимы обычные соображения применения высокоуровневых инструментов. Т.е. каждый решает за себя в контексте целей. Например, проверка числа на простоту средствами LINQ-а будет весьма наглядна и декларативна, но это будет не лучшее решение в аспекте производительности. Применять ли в данном случае LINQ — следует решать исходя из целей. Если стоит цель написать легкоподдерживаемый и наглядный код, то да, LINQ целесообразен. Если стоит цель выжать производительность до такта, то ответ — нет.
Переформулирую вопрос немного иначе. К каким структурам данных его стоит применять ? У меня некоторое ощущение (можеТ , я не прав), что он "ложится" на "линейные контейнеры" (термин очень неточный, я имею в виду нечто такое, что более или менее линейно перечислимо, и дело даже не в том, что перечислимо, а в том, что это перечисление именно то, что нужно в этой задаче). А вот насколько он применим к нелинейным конструкциям ? Например, алгоритмы работы с деревьями, графами ?
Здравствуйте, samius, Вы писали:
S>Если нужно такое понимание, то следует сделать Extract Method с параметром типа Stream.
Понимание того, что делаешь, нужно всегда, а выделять метод на каждое открытие файла, при последующем использовании которого не используются специфические для FileStream методы — overkill. Или ты имеешь ввиду один универсальный CreateFileStreamAsStream?
Кроме того внутри вновь созданного метода получим ту же ситуацию, что имели: чтобы узнать, что при использовании созданного FileStream используются только методы Stream, нужно будет просмотреть код. Так зачем тогда?
Здравствуйте, Mystic, Вы писали:
M>Я обращал внимание на то, что оптимизация, связанная с многопоточностью, уже может быть выполнена уровнем выше. Например, у нас есть сервер, есть пул потоков, каждая сессия работает в своем собственном потоке. Если при этом еще и сессия начнет плодить потоки дл организации вычислений, то работа сервера в целом может замедлится.
А что делать сессии, если у нее алгоритм обработки запроса частично последовательный, а частично параллельный?
Самой обращаться к пулу потоков и ручками в этих потоках запускать свои куски?
M>Или, например, при написании шахматной прораммы, нет никакого смысла делать генератор ходов многопоточным, потому что распараллеливание естественным образом возникает при переборе вариантов на верхнем уровне.
Запустить несколько генераторов в отдельных потоках, отрезав каждому по ломтику задачи?
M>Вообще, потребность в оптимизации вычислительных алгоритмов возникает не часто. И первый шаг в такой оптимиации происходит на алгоритмическом уровне: вместо того, чтобы выполнять циклы быстро и параллельно, мы избавляемся от циклов, строим заранее таблицы для часто повторяющихс вычислений и т. д. и т. п.
M>Я тут скорее всего даже не про распараллеливание как таковое, потому как для меня это самый последний шаг в оптимизации. Вначале надо выжать по максимуму из самого алгоритма. А уже потом параллелизм на последнем шаге. И в четко выбранном месте.
Сложно сказать. В принципе, сейчас я тоже к параллелизму прихожу как к крайнему средству. Но это происходит не в последнюю очередь потому, что средства распараллеливания сейчас очень неуклюжи. Их применение обфусцирует предметный алгоритм. Однако многоядерность твердой поступью входит в нашу жизнь, появляются новые средства обеспечения параллельных вычислений, так что приоритеты оптимизации меняются. По-этому я активно интересуюсь прогрессом в этой области.
M>Вот большая проблема как раз в правильном выборе. Возникают "дыры", с которыми потом усиленно борешься.
Это скорее к вопросу о том, как правильно проектировать программу. Нельзя отказываться абстрагирования только потому, что это при неправильном применении заводит в тупик.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Переформулирую вопрос немного иначе. К каким структурам данных его стоит применять ? У меня некоторое ощущение (можеТ , я не прав), что он "ложится" на "линейные контейнеры" (термин очень неточный, я имею в виду нечто такое, что более или менее линейно перечислимо, и дело даже не в том, что перечислимо, а в том, что это перечисление именно то, что нужно в этой задаче). А вот насколько он применим к нелинейным конструкциям ? Например, алгоритмы работы с деревьями, графами ?
Применим. Причем применим не только к структурам данных аки списки деревья и графы. Он применим к асинхронным вычислениям, к реактивным вычислениям, к синтаксическому разбору и т.п.
Здравствуйте, samius, Вы писали:
S>Применим. Причем применим не только к структурам данных аки списки деревья и графы. Он применим к асинхронным вычислениям, к реактивным вычислениям, к синтаксическому разбору и т.п.
Кстати, о синтаксическом разборе. Есть такие примеры?
Здравствуйте, igna, Вы писали:
I>Здравствуйте, samius, Вы писали:
S>>Если нужно такое понимание, то следует сделать Extract Method с параметром типа Stream.
I>Понимание того, что делаешь, нужно всегда, а выделять метод на каждое открытие файла, при последующем использовании которого не используются специфические для FileStream методы — overkill. Или ты имеешь ввиду один универсальный CreateFileStreamAsStream?
Повышение абстракции помогает тестированию, например. Когда я пишу метод чтения либо записи чего-то в файл/из файла, я сразу думаю, а как я его буду тестировать. Работать с MemoryStream-ом в тестах удобнее, чем с файлами, потому я для начала создам метод, работающий с абстракцией стрима, потом сделаю метод, создающий файл-стрим и делегирующий более абстрактному методу, который работает со стримом.
I>Кроме того внутри вновь созданного метода получим ту же ситуацию, что имели: чтобы узнать, что при использовании созданного FileStream используются только методы Stream, нужно будет просмотреть код. Так зачем тогда?
Можно будет посмотреть только на сигнатуру метода и убедиться что внутри нет даункаста.
Здравствуйте, samius, Вы писали:
S>Применим. Причем применим не только к структурам данных аки списки деревья и графы. Он применим к асинхронным вычислениям, к реактивным вычислениям, к синтаксическому разбору и т.п.
Можно пример функции, скажем, нахождения минимального элемента в двоичном дереве ? Или классическую SearchAndInsert в несбалансированном двоичном дереве поиска ?
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, samius, Вы писали:
S>>Применим. Причем применим не только к структурам данных аки списки деревья и графы. Он применим к асинхронным вычислениям, к реактивным вычислениям, к синтаксическому разбору и т.п.
PD>Можно пример функции, скажем, нахождения минимального элемента в двоичном дереве ?
Можно пойти двумя путями: сведение пути по дереву к последовательности и применения стандартных методов для IEnumerable<T>, либо написание своей обвязки для типа Node<T>.
Вот первый вариант:
class Node<T>
{
public T Value { get; set; }
public Node<T> Left { get; set; }
public Node<T> Right { get; set; }
}
class Program
{
static IEnumerable<T> Walk<T>(T root, Func<T, IEnumerable<T>> next)
{
yield return root;
var q = from node in next(root)
from n in Walk(node, next)
select n;
foreach (var node in q)
{
yield return node;
}
}
static void Main()
{
Node<int> root = new Node<int>
{
Value = 3,
Left = new Node<int>
{
Value = 2,
Left = new Node<int> { Value = 1 }
},
Right = new Node<int> { Value = 4 }
};
var minNode = Walk(
root,
n => n.Left != null? new[] {n.Left} : new Node<int>[]{})
.Last();
Console.WriteLine(minNode.Value);
}
}
Метод Walk может показаться громоздким для решения этой задачи, но вообще у него офигительный реюз, он сгодится не только здесь, а например и для выпрямления в последовательность дерева каталогов и вообще деревьев. Вообще не понимаю, почему нет аналога в Enumerable.
Второй способ опущу.
PD>Или классическую SearchAndInsert в несбалансированном двоичном дереве поиска ?
Insert — это не запрос. Здесь нужно либо модификация существующего дерева, либо построение нового дерева. LINQ тут не совсем по назначению, хоть и присобачить его можно.
Здравствуйте, samius, Вы писали:
PD>>Можно пример функции, скажем, нахождения минимального элемента в двоичном дереве ? S>Можно пойти двумя путями: сведение пути по дереву к последовательности и применения стандартных методов для IEnumerable<T>, либо написание своей обвязки для типа Node<T>.
S>Вот первый вариант:
Может, я не так понимаю, но ИМХО ты здесь еще один контейнер создал, q. Пусть временный, но создал.
S> var minNode = Walk( S> root, S> n => n.Left != null? new[] {n.Left} : new Node<int>[]{}) S> .Last();
И вот это мне совсем не по душе. Задача — найти минимальный элемент в дереве, а не выложить его куда-то упорядоченным и взять последний элемент. Задача нахождения минимального элемента в дереве алгоритмически не требует ни одного копирования элементов.
S>Метод Walk может показаться громоздким для решения этой задачи, но вообще у него офигительный реюз, он сгодится не только здесь, а например и для выпрямления в последовательность дерева каталогов и вообще деревьев. Вообще не понимаю, почему нет аналога в Enumerable.
Я не спорю, но ты решил иную задачу Реюз-то реюз, но... как бы объяснить без ссылок на производительность. Попробую так. Вот есть алгоритмы сортировки. И все договорились — отсортировать надо исходный массив без дополнительной памяти O(N). Иными словами, решения с еще одним массивом не рассматриваются. А для обхода деревьев не рассматриваются алгоритмы, которые копируют элементы деревьев — куда бы то ни было.
Такое можно ?
S>Insert — это не запрос. Здесь нужно либо модификация существующего дерева, либо построение нового дерева.
Зачем же новое ? Но это к слову.
А вот что не к слову — здесь не чистый запрос, да.
Но вначале чистый запрос — есть ли элемент. Если не SearchAndInsert, а просто Search — это уж точно запрос. И если ты собираешься его реализовать чем-то вроде выкладывания как в прошлом примере и поиском там — извини, но что это за алгоритм поиска в двоичном дереве, который требует его выкладывания в последовательность ? На кой мне черт такое дерево ? Мне log(N) подайте и без копирования!
А во вторых, тут очень показательный момент. Без Linq SearchAndInsert отличается от Search 2 строчками (либо return nul, либо return new node(...)) А так получится, что допустим, предполагал я , что вставки не понадобятся, написал на LinQ, а они потом понадобились, и что теперь ?
Здравствуйте, Dufrenite, Вы писали:
D>Все остальные рассуждения на изначально неверном предположении.
Допустим, я не прав, но ... какая-то еще структура создается в памяти или или нет ? Запросом, контейнером, чертом лысым — но да или нет ? Потому что я не понимаю, как можно Last делать в дереве — ну нет там Last. А сами элементы копируются или нет ? Если нет — то как это все же работает ? Лежат они как попало в памяти, а тут вдруг оказались выложенными в линию. Одно из двух — либо в памяти создан контейнер со ссылками на них, либо их скопировали в другой контейнер и там они подряд лежат.
Не согласен ? Тогда объясни, что там под спудом. Что там делается, какие данные и куда создаются или копируются.
Здравствуйте, samius, Вы писали:
I>>Кроме того внутри вновь созданного метода получим ту же ситуацию, что имели: чтобы узнать, что при использовании созданного FileStream используются только методы Stream, нужно будет просмотреть код. Так зачем тогда?
S>Можно будет посмотреть только на сигнатуру метода и убедиться что внутри нет даункаста.
"Внутри вновь созданного метода" у тебя переменная и так будет типа FileStream, какой даункаст?
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Допустим, я не прав, но ... какая-то еще структура создается в памяти или или нет ? Запросом, контейнером, чертом лысым — но да или нет ?
Создается, безусловно. Что создается конкретно можно увидеть декомпилировав код.
PD>Потому что я не понимаю, как можно Last делать в дереве — ну нет там Last.
Last, это extension-метод IEnumerable<T> — результата запроса.
PD>А сами элементы копируются или нет ?
Только как возвращаемое значение свойства IEnumerator<T>.Current.
PD>Если нет — то как это все же работает ? Лежат они как попало в памяти, а тут вдруг оказались выложенными в линию. Одно из двух — либо в памяти создан контейнер со ссылками на них, либо их скопировали в другой контейнер и там они подряд лежат.
PD>Не согласен ? Тогда объясни, что там под спудом. Что там делается, какие данные и куда создаются или копируются.
Там все намного проще. Надеюсь с концепцией итераторов знакомы? Так вот енумератор, это такой продвинутый, генерируемый компилятором (или написанный вручную) итератор.
В данном случае q, это фактически объект, реализующий интерфейс IEnumerable<T>, в котором есть метод, возвращающий итератор.