Трансформация коллекции
От: tyomchick Россия  
Дата: 28.05.15 14:52
Оценка:
В общем есть необходимость выполнять массовое изменение элементов коллекции.
Ну например элементы байтового массива перегнать в BCD или обратно.

Породил такие хелперы:
  Код
        /// <summary>
        /// Преобразует элементы списка 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="self"></param>
        /// <param name="transformFunc">Функция преобразование элемента</param>
        public static void Transform<T>([NotNull] this IList<T> self, [NotNull] Func<T, T> transformFunc)
        {
            Transform(self, 0, self.Count, transformFunc);
        }

        /// <summary>
        /// Преобразует элементы части списка 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="self"></param>
        /// <param name="startIndex">Начальный индекс области преобразования</param>
        /// <param name="count">Количество преобразуемых элементов</param>
        /// <param name="transformFunc">Функция преобразования элемента</param>
        public static void Transform<T>([NotNull] this IList<T> self, int startIndex, int count, [NotNull] Func<T, T> transformFunc)
        {
            var endIndex = startIndex + count;
            for (var i = startIndex; i < endIndex; i++)
                self[i] = transformFunc(self[i]);
        }

Но чувствую, что нарушаю какие то концепции, чем могу ввести в заблуждение пользователей библиотеки.
Но как сделать красиво не знаю.
Может подскажете?
Даже самую простую задачу можно сделать невыполнимой, если провести достаточное количество совещаний
Re: Трансформация коллекции
От: RushDevion Россия  
Дата: 28.05.15 15:02
Оценка:
А чем стандартный LINQ Select не устроил (или Select+Skip/Take для подмножества)?
Re[2]: Трансформация коллекции
От: tyomchick Россия  
Дата: 28.05.15 15:13
Оценка:
Здравствуйте, RushDevion, Вы писали:

RD>А чем стандартный LINQ Select не устроил (или Select+Skip/Take для подмножества)?


Не понял. Они же IEnumerable возвращают, а я меняю элементы коллекции и ничего не возвращаю.
Даже самую простую задачу можно сделать невыполнимой, если провести достаточное количество совещаний
Re: Трансформация коллекции
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 28.05.15 15:24
Оценка: :)
Здравствуйте, tyomchick, Вы писали:

T>Но чувствую, что нарушаю какие то концепции,


Любым изменением данных ты нарушаешь принцип иммутабельности
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[2]: Трансформация коллекции
От: tyomchick Россия  
Дата: 28.05.15 16:29
Оценка:
Здравствуйте, xvost, Вы писали:


X>Любым изменением данных ты нарушаешь принцип иммутабельности


Ну в общем это понятно, но метод таки нужен. И вопрос в том, как его кошерно оформить.
Просто статическим методом?
Даже самую простую задачу можно сделать невыполнимой, если провести достаточное количество совещаний
Re: Трансформация коллекции
От: Sinix  
Дата: 28.05.15 16:55
Оценка: 8 (2)
Здравствуйте, tyomchick, Вы писали:

T>В общем есть необходимость выполнять массовое изменение элементов коллекции.

T>Ну например элементы байтового массива перегнать в BCD или обратно.

Правильные хелперы всегда начинаются со сценариев использования. В общем случае разница между
for (int i = 2; i < 2 + 3; i++)
{
  list[i] = SomeCode(list[i]);
}
// и
list.Update(2, 3, x => SomeCode(x));

очевидна, так что смысл использовать есть. Пара "стартовый индекс + количество" тоже абсолютно правильная, полный аналог Skip+Take. Вариант с стартовым/конечным индексом в API дотнета практически не используется. Неудобно.

Разумеется, если SomeCode раскрывается в длинное выражение или нужен отбор, break или continue — проще не страдать фигнёй и запустить обычный for.
Переписать в любой из вариантов дело 5 минут, так что сильно заморачиваться "правильно/неправильно" тут не стоит.

Пара замечаний:
1. Я бы поменял имя метода на Update. Transform обычно используется как "чистая" функция, которая возвращает новый результат. Вариант с Replace тоже неплох.
2. Неплохо бы добавить дебаг-ассерты для индексов и для проверки на null. Всегда лучше, когда ошибка всплывает сразу, а не в середине перебора.

Ну и на отрицательный count ассерт нужен в любом случае, это баг. И checked() на вычисление endIndex. Я бы добавил ассерт и на count == 0 (или возвращал бы false, если перебор не удался). Если окажется, что большинство случаев с count==0 — не ошибка, всегда можно будет убрать.
Re: Трансформация коллекции
От: hardcase Пират http://nemerle.org
Дата: 28.05.15 18:15
Оценка: +1
Здравствуйте, tyomchick, Вы писали:

T>В общем есть необходимость выполнять массовое изменение элементов коллекции.


По большим массивам ходить через IList получается очень медленно и печально.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[2]: Трансформация коллекции
От: hardcase Пират http://nemerle.org
Дата: 28.05.15 18:23
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Правильные хелперы всегда начинаются со сценариев использования. В общем случае разница между

S>
S>for (int i = 2; i < 2 + 3; i++)
S>{
S>  list[i] = SomeCode(list[i]);
S>}
S>// и
S>list.Update(2, 3, x => SomeCode(x));
S>

S>очевидна, так что смысл использовать есть.

Мне вот очевиднее, что for версия уместнее — она замыкания не будет порождать.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[3]: Трансформация коллекции
От: Sinix  
Дата: 28.05.15 19:24
Оценка:
Здравствуйте, hardcase, Вы писали:

H>Мне вот очевиднее, что for версия уместнее — она замыкания не будет порождать.

Угу. Два но:
* Как дойдёт до "лямбды тормозят", переписать — 5 минут.
* И до этого ещё дожить надо.
Re[2]: Трансформация коллекции
От: tyomchick Россия  
Дата: 29.05.15 06:54
Оценка:
Здравствуйте, Sinix, Вы писали:


S>Пара замечаний:

S>1. Я бы поменял имя метода на Update. Transform обычно используется как "чистая" функция, которая возвращает новый результат. Вариант с Replace тоже неплох.

Да, пожалуй Update более удачное название.

S>2. Неплохо бы добавить дебаг-ассерты для индексов и для проверки на null. Всегда лучше, когда ошибка всплывает сразу, а не в середине перебора.


Это решается атрибутом [NotNull], решарперским анализатором, и административным запретом на комиты с решарперскими варнингами.

S>Ну и на отрицательный count ассерт нужен в любом случае, это баг. И checked() на вычисление endIndex.


Да, вы правы, но наверное я всё таки не дебажные ассерты, а исключения ArgumentOutOfRangeException кидать буду.

S>Я бы добавил ассерт и на count == 0 (или возвращал бы false, если перебор не удался). Если окажется, что большинство случаев с count==0 — не ошибка, всегда можно будет убрать.


Думаю всё же не буду. Вроде бы все функции что я знаю, лояльно относятся к нулевой длине, не хотелось бы рвать шаблон.
Даже самую простую задачу можно сделать невыполнимой, если провести достаточное количество совещаний
Отредактировано 29.05.2015 9:05 tyomchick . Предыдущая версия .
Re: Трансформация коллекции
От: TK Лес кывт.рф
Дата: 29.05.15 09:10
Оценка:
Здравствуйте, tyomchick, Вы писали:

T>В общем есть необходимость выполнять массовое изменение элементов коллекции.

T>Ну например элементы байтового массива перегнать в BCD или обратно.

T>Породил такие хелперы:

T>Но чувствую, что нарушаю какие то концепции, чем могу ввести в заблуждение пользователей библиотеки.

Можно заменить хелперы на врапперы:
        public static IReadonlyList<T> Transform<T>([NotNull] this IList<T> self, [NotNull] Func<T, T> transformFunc)
        {
            return new TransformedList(self, transformFunc);
        }


Вызов transform будет выполняться за константное время, не надо думать что произойдет если трасформировать один и тот же список дважды.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[4]: Трансформация коллекции
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 03.06.15 19:26
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Угу. Два но:

S>* Как дойдёт до "лямбды тормозят", переписать — 5 минут.
S>* И до этого ещё дожить надо.
Против твоего подхода есть одно "но" — а нафига козе баян? Чтобы не писать цикл в три строчки кода? Или есть какие-то более ощутимые бенефиты?
[КУ] оккупировала армия.
Re: Трансформация коллекции
От: Evgeny.Panasyuk Россия  
Дата: 03.06.15 19:45
Оценка:
Здравствуйте, tyomchick, Вы писали:

T>Может подскажете?


В C++ STL есть подобный алгоритм std::transform — отличие от твоего в том что ещё есть возможность выбрать куда записывать трансформированные значения (а не только self), работает для итераторов начиная от Input/Output (то есть single pass, а не random access) и возвращает output iterator (если под ним single pass/forward, чтобы знать откуда дальше продолжать).
Плюс в Boost есть обвёртки с меньшим числом параметров (не нужно писать begin/end для типичных случаев).
Re[5]: Трансформация коллекции
От: Evgeny.Panasyuk Россия  
Дата: 03.06.15 20:58
Оценка: +1
Здравствуйте, koandrew, Вы писали:

K>Против твоего подхода есть одно "но" — а нафига козе баян? Чтобы не писать цикл в три строчки кода? Или есть какие-то более ощутимые бенефиты?


* Такой же интерфейс имеет parallel_transform (например в Microsoft PPL, libstdc++ parallel) — то есть легко распараллелить при необходимости.
* Внутри могут быть оптимизации которые заняли бы не три строчки, типа loop unroll.
* При чтении кода когда видишь transform — то сразу понятно что там происходит, не нужно вчитываться что же там именно итерируется и нет ли там внутри изменения индекса или какого-нибудь break/return/continue.
* Цикломатическая сложность кода уменьшается.
* Намного проще решать/обсуждать/обдумывать задачу в терминах готовых алгоритмов, а не голых циклов. Sean Parent в своей презентации даже даёт такой guideline — no raw loops:
http://www.youtube.com/watch?v=qH6sSOr-yk8
Re[5]: Трансформация коллекции
От: Sinix  
Дата: 04.06.15 05:59
Оценка:
Здравствуйте, koandrew, Вы писали:

K>Против твоего подхода есть одно "но" — а нафига козе баян? Чтобы не писать цикл в три строчки кода? Или есть какие-то более ощутимые бенефиты?

Это топикстартера подход, я только про "дизайн API" отвечал
Если такая штука встречается по коду десятки раз, то это повод выбросить и переписать вытащить однотипный код в хелпер.

С моей точки зрения — тут поиск приключений на ровном месте. Т.к. такие одномоментные хелперы при долгой жизни проекта мутируют в убер-фреймворки, а их лучше убивать, пока они маленькие.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.