Здравствуйте, Gaperton, Вы писали:
L>>Она решается без протягивания состояния исключительно потому, что в твоем решении сосотояние глобально.
G>В этом решении нет состояния вообще, потому, что в нем нет мутабельных операций. Все абсолютно чисто. Я нигде не изменяю состояния создаваемых объектов, и ничего помнить между итерациями не требуется.
Что-то я не улавливаю связи между неличием состояния и мутабельными операциями. В моем последнем примере с линком состояние было, но не было мутабельных операций.
L>>В случае, когда у тебя будет только курсор, который можно прокрутить только вперед (IEnumerable) без состояния уже не обойтись, как минимум придется сохранять значение предиката на предыдущем элементе.
G>Ты имеешь в виду, при невозможности обращения к исходному массиву по индексу, штоли? Да вроде и этого можно избежать, при желании. Все должно ложиться на однопроходные операции, а обращение по индексу может быть решено через джойны.
Объясни, что ты имеешь в виду. Что зв join?
G>Интерес, правда, в этом скорее академический. Уж больно через жопу как-то получается. Но надо на всякий случай запомнить, может пригодится когда-нибудь.
Почему "академический"? Бесконечные последовательности по-моему вполне реальный пример. Разве нет? А по ним по индексу не побегаешь.
Здравствуйте, Геннадий Васильев, Вы писали:
L>>А можно уточнить, какие сложности при этом возникают. Не втыкаю как-то.
ГВ>Не так просто привязаться к позиции. Например, у нас идут вот такие записи:
ГВ>
ГВ>struct R {
ГВ> DateTime time;
ГВ> int value;
ГВ> bool mark;
ГВ> ...
ГВ>};
ГВ>
ГВ>...в порядке возрастания time. Некоторые содержат true в mark. Когда на вход поступает очередная запись с mark=true (обозначим её r0), нужно выдать на выход "самую старую" запись, пришедшую не ранее, чем за 30 минут. Задача усложняется тем, что совершенно не обязательно, что time будет выравнен по границе минуты, т.е. к индексу уже не привяжешься.
Кешировать данные за последние тридцать минут, отбрасывая по мере изменения последнего time, и при появлении mark-а возвращать последюю. Или я опять чего-то недогоняю?
Здравствуйте, Lloyd, Вы писали:
L>Кешировать данные за последние тридцать минут, отбрасывая по мере изменения последнего time, и при появлении mark-а возвращать последюю. Или я опять чего-то недогоняю?
Всё верно. Теперь, пожалуйста, то же самое с помощью LINQ?
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Геннадий Васильев, Вы писали:
L>>Кешировать данные за последние тридцать минут, отбрасывая по мере изменения последнего time, и при появлении mark-а возвращать последюю. Или я опять чего-то недогоняю?
ГВ>Всё верно. Теперь, пожалуйста, то же самое с помощью LINQ?
Что-то мне подсказывает, что тут можно как-то использовать новомодный rx (linq to events), потому как по сути речь идеть о преобразовании одних событий в другие.
Но эта область для меня пока темный лес.
Здравствуйте, Lloyd, Вы писали:
G>>В этом решении нет состояния вообще, потому, что в нем нет мутабельных операций. Все абсолютно чисто. Я нигде не изменяю состояния создаваемых объектов, и ничего помнить между итерациями не требуется.
L>Что-то я не улавливаю связи между неличием состояния и мутабельными операциями. В моем последнем примере с линком состояние было, но не было мутабельных операций.
"Изменяемое состояние" обычно связывают с отсутствием "чистоты". Связь самая прямая. Я не понимаю, что ты в данном случае называешь глобальным состоянием, может быть пояснишь?
В любом случае, при описываемом мной подходе, итерации независимы, и передавать от одной к другой ничего не требуется. Это будет очевидно, если посмотреть на реализацию, где компрехеншны развернуты в циклы.
L>>>В случае, когда у тебя будет только курсор, который можно прокрутить только вперед (IEnumerable) без состояния уже не обойтись, как минимум придется сохранять значение предиката на предыдущем элементе.
G>>Ты имеешь в виду, при невозможности обращения к исходному массиву по индексу, штоли? Да вроде и этого можно избежать, при желании. Все должно ложиться на однопроходные операции, а обращение по индексу может быть решено через джойны.
L>Объясни, что ты имеешь в виду. Что зв join?
Аналог моей операции для генераторов & (zip, сделать из двух последовательностей одну последовательность пар, либо просто синхронно пробежаться по двум впараллель) у вас есть, или нет? Если да, то ты можешь сгенерировать последовательность индексов, и использовать так, как используют ключи в реляционной БД. Клеишь эти индексы к значениям а, чтобы парами шли. Дальше — джойнами работаешь, схожими приемами, как в SQL. Вообще я затрудняюсь это более понятно объяснить.
Не слишком эффективно, но сработать должно.
Что касается построения первых двух множеств — там все еще проще, потому как известна "глубина просмотра" назад на каждой итерации, и она постоянна. Берешь исходный массив, сдвигаешь его, клеишь с исходным, и вперед.
G>>Интерес, правда, в этом скорее академический. Уж больно через жопу как-то получается. Но надо на всякий случай запомнить, может пригодится когда-нибудь.
L>Почему "академический"? Бесконечные последовательности по-моему вполне реальный пример. Разве нет? А по ним по индексу не побегаешь.
Бесконечные? Давай я подумаю на этот счет, их я пока не рассматривал. Сейчас уже ничего не соображаю, потом.
Здравствуйте, Lloyd, Вы писали:
ГВ>>Всё верно. Теперь, пожалуйста, то же самое с помощью LINQ? L>Что-то мне подсказывает, что тут можно как-то использовать новомодный rx (linq to events), потому как по сути речь идеть о преобразовании одних событий в другие.
Ну, как-то приблизительно смахивает. Если поглядеть в эту статью, то Linq2Events ну почти что подбирается к модели CSP. Подбирается как "фашисты к Севастополю", через пень-колоду, но тем не менее.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Gaperton, Вы писали:
L>>Объясни, что ты имеешь в виду. Что зв join?
G>Аналог моей операции для генераторов & (zip, сделать из двух последовательностей одну последовательность пар, либо просто синхронно пробежаться по двум впараллель) у вас есть, или нет? Если да, то ты можешь сгенерировать последовательность индексов, и использовать так, как используют ключи в реляционной БД.
Да, да, при таком подходе внутри генераторов индексов у тебя будет от итерации к итерации протягиваться щотчик. Но это нормально и как-бы не считается. Потому, что эта передача хорошо изолирована где-то на окраине, и на остальное не влияет.
Здравствуйте, Gaperton, Вы писали:
G>>>В этом решении нет состояния вообще, потому, что в нем нет мутабельных операций. Все абсолютно чисто. Я нигде не изменяю состояния создаваемых объектов, и ничего помнить между итерациями не требуется.
L>>Что-то я не улавливаю связи между неличием состояния и мутабельными операциями. В моем последнем примере с линком состояние было, но не было мутабельных операций.
G>"Изменяемое состояние" обычно связывают с отсутствием "чистоты". Связь самая прямая.
Опять какая-то путаница. Понятно, что для мутабельнсости, нужно состояние, т.к. если нет состояния, то и менять нечего. Но у тебя вывод-то дурой: состояния нет, т.к. нет мутабельных операций. Причина со следствие переставлены.
G>Я не понимаю, что ты в данном случае называешь глобальным состоянием, может быть пояснишь?
Поясню: массив, из кторого ты читаешь.
G>>>Ты имеешь в виду, при невозможности обращения к исходному массиву по индексу, штоли? Да вроде и этого можно избежать, при желании. Все должно ложиться на однопроходные операции, а обращение по индексу может быть решено через джойны.
L>>Объясни, что ты имеешь в виду. Что зв join?
G>Аналог моей операции для генераторов & (zip, сделать из двух последовательностей одну последовательность пар, либо просто синхронно пробежаться по двум впараллель) у вас есть, или нет?
Есть, более того, он (Zip) был использован в моем примере.
G>Если да, то ты можешь сгенерировать последовательность индексов, и использовать так, как используют ключи в реляционной БД. Клеишь эти индексы к значениям а, чтобы парами шли. Дальше — джойнами работаешь, схожими приемами, как в SQL. Вообще я затрудняюсь это более понятно объяснить.
Опять не понимаю. Можно кодом?
G>Не слишком эффективно, но сработать должно.
G>Что касается построения первых двух множеств — там все еще проще, потому как известна "глубина просмотра" назад на каждой итерации, и она постоянна. Берешь исходный массив, сдвигаешь его, клеишь с исходным, и вперед.
Это и будет "простаскиванием" состояния.
G>>>Интерес, правда, в этом скорее академический. Уж больно через жопу как-то получается. Но надо на всякий случай запомнить, может пригодится когда-нибудь.
L>>Почему "академический"? Бесконечные последовательности по-моему вполне реальный пример. Разве нет? А по ним по индексу не побегаешь.
G>Бесконечные? Давай я подумаю на этот счет, их я пока не рассматривал. Сейчас уже ничего не соображаю, потом.
Здравствуйте, Gaperton, Вы писали:
G>В чем я действительно сомневаюсь — это в том, что Linq проявляет себя в полную силу в классе приложений, отличном от того, где у тебя в бэкенде стоит реляционная БД. Мне кажется, что его настоящий killing application — это приложения с реляционным источником.
Что такое реляционный источник?
G>Это по своей природе типичная "реляционная" задача. И это весьма показательно, что силу linq ты предпочел демонстрировать на ней.
Если я правильно понимаю то, что ты понимаешь под термином "реляционная", то таких задач подавляющее большинство, а те которые таковыми не являются возможно свести к такой форме.
G>Работая с Go, я просто заверну это в хранимую процедуру, как делал еще в 90-х, и выиграю в производительности, одновременно абстрагировавшись от схемы БД. И все.
Это задача является частью процесса, который как раз был вынесен из БД в целях улучшения производительности. Дело в том что аллокирование затрат может происходить на тех, с кого нужно это опять списать, поэтому расчёт делается множетсвом итераций пока у нас всё не распределиться. Учитывая, что речь идёт о количестве обрабатываемых записей в десятки миллионов, то одна итерация в БД занимала примерно 20 минут, а итерация в памяти около 10 секунд. В результате оказывается быстрее посчитать всё в памяти и потом залить результат через bcp, чем крутить циклы в SQL.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Gaperton, Вы писали:
ГВ>>>Вторая задача, кстати, тоже не совсем правильно решена — из-за Union могут быть проглочены соседние элементы, например на вот таком массиве { 1, 2, 3, 3, 4, 4, 5, 1, 1, 2 } ответ получается {1, 3, 4}, а должен быть как минимум {1, 3, 3, 4}.
IT>>Я задачу понял именно так. Для твоего уточнения достаточно заменить Union на Cancat. И что значит выделенное, первая задача решена не правильно?
G>Первая задача решена может и правильно, но однозначно чудовищно.
О чудовищности мы можем поговорить отдельно. Главное не это. Мы то все понимаем, что расчёт ГВ был прост и незатейлив — учитывая, что Linq работает с последовательностями, предложить задачу, в которой требуется обращение к предыдущему/последующему элементам, да ещё необходимо сохранять промежуточное состояние. В принципе, расчёт был верен, да вот только Linq не запрещает ни использование состояния, ни счётчиков, ни всего остального. В реальных же задачах неумение свести задачу к обработке последовательности в большинстве случаев говорит лишь о неумении это делать. foreach в C# не просто так придумали, это как раз говорит о том, что большинству алгоритмов даже номер элемента в последовательности знать совсем не обязательно. А случаи, где это нужно знать как правило работают с двумя и более последовательностями и индекс нужен просто потому, что по другому это выразить нельзя. Хотя можно склеить две последовательности и получить тот же эффект и без индексов.
G>Зато вторая — неиллюзорно доставляет. Заставляет меня вспомнить молодые годы, когда мы по неопытности любили использовать SQL весьма необычным способом. Так, что читателей бросало в дрожь. Помню, когда мой первый начальник увидел мой первый "промышленный" SQL-запрос, он после этого наотрез отказался изучать SQL. И никто так и не смог его переубедить.
G>В общем, так держать!
Ты тут постом выше предложил вести разговор в продуктивном ключе или это тебя самого не касалось?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Gaperton, Вы писали:
IT>>Ты сильно не зарывайся. Обсуждение инквизиции запрещено правилами форума, за это можно на сутки-двое и на костерок загреметь.
G>Точно. Запрещено секретным пунктом правил. И в его подпункте написано, что IT лишен чувства юмора.
Если бы не моё чувство юмора вы бу уже все давно горели синим пламенем.
Если нам не помогут, то мы тоже никого не пощадим.
ГВ>static IEnumerable<int> sample1b(IEnumerable<int> arr)
ГВ>{
ГВ> bool first = true;
ГВ> int prev = 0;
ГВ> foreach (int i in arr)
ГВ> {
ГВ> if (first)
ГВ> {
ГВ> first = false;
ГВ> prev = i;
ГВ> }
ГВ> else if (i - prev == 1)
ГВ> {
ГВ> yield return i;
ГВ> first = true;
ГВ> }
ГВ> else prev = i;
ГВ> }
ГВ>}
ГВ>
ГВ>Можно это как-то сделать средствами LINQ?
Where как и любой другой метод Linq — это и есть решение с помощью yield return. Поставь в месте где у тебя foreach arr.Where и получишь с небольшими модификациями практически тоже самое. Там где у тебя yield return будет return true, в остальных местах return false.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Where как и любой другой метод Linq — это и есть решение с помощью yield return. Поставь в месте где у тебя foreach arr.Where и получишь с небольшими модификациями практически тоже самое. Там где у тебя yield return будет return true, в остальных местах return false.
Хммм... А с состоянием что делать? Может, лучше ты сам покажешь?
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Геннадий Васильев, Вы писали:
IT>>Where как и любой другой метод Linq — это и есть решение с помощью yield return. Поставь в месте где у тебя foreach arr.Where и получишь с небольшими модификациями практически тоже самое. Там где у тебя yield return будет return true, в остальных местах return false.
ГВ>Хммм... А с состоянием что делать? Может, лучше ты сам покажешь?
var q1 = arr
.Where(((Func<Func<int, bool>>)(() => {
bool first = true;
int prev = 0;
return i => {
if (first) {
first = false;
prev = i;
return false;
} else if (i - prev == 1) {
first = true;
return true;
} else {
prev = 1;
return false;
}
};
}))());
Здравствуйте, Lloyd, Вы писали:
ГВ>>Хммм... А с состоянием что делать? Может, лучше ты сам покажешь?
L>
L>var q1 = arr
L> .Where(((Func<Func<int, bool>>)(() => {
L> bool first = true;
L> int prev = 0;
L> return i => {
L> if (first) {
L> first = false;
L> prev = i;
L> return false;
L> } else if (i - prev == 1) {
L> first = true;
L> return true;
L> } else {
L> prev = 1;
L> return false;
L> }
L> };
L> }))());
L>
L>
Осталось только выяснить, почему на наборе { 1, 2, 3, 3, 4, 4, 5, 1, 1, 2 } на выходе получается {2, 2}.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, IT, Вы писали:
G>>Первая задача решена может и правильно, но однозначно чудовищно. IT>О чудовищности мы можем поговорить отдельно.
Собственно, это главная фишка.
IT>Главное не это. Мы то все понимаем, что расчёт ГВ был прост и незатейлив — учитывая, что Linq работает с последовательностями, предложить задачу, в которой требуется обращение к предыдущему/последующему элементам, да ещё необходимо сохранять промежуточное состояние. В принципе, расчёт был верен, да вот только Linq не запрещает ни использование состояния, ни счётчиков, ни всего остального.
Всё так. Linq и в самом деле не запрещает использовать состояния, счётчики и всё прочее. Как бы это он смог что-то запретить, когда мы располагаем ссылками на объекты и таким артефактом, как не зависимые от Linq области видимости имён? Тут дело в другом. Пока что из показанных Linq-примеров самые компактные являются гипертрофированными операторными скобками к обычным циклам. Это не считая того, что в этих самых "циклах" ещё и примешаны лямбды. Если такая запись называется упрощением цикла, то кто-то что-то очень сильно путает.
IT>В реальных же задачах неумение свести задачу к обработке последовательности в большинстве случаев говорит лишь о неумении это делать. foreach в C# не просто так придумали, это как раз говорит о том, что большинству алгоритмов даже номер элемента в последовательности знать совсем не обязательно. А случаи, где это нужно знать как правило работают с двумя и более последовательностями и индекс нужен просто потому, что по другому это выразить нельзя. Хотя можно склеить две последовательности и получить тот же эффект и без индексов.
Игорь, давай без ссылок на большинство, реальность и разные "как правило". Вот здесь
приведён пример, построенный на упрощённой реальной задаче. Как думаешь, получится решить его на Linq компактнее, чем циклами?
IT>[...] вести разговор в продуктивном ключе [...]
Двумя руками "за". Show me your code, ok?
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
приведён пример, построенный на упрощённой реальной задаче. Как думаешь, получится решить его на Linq компактнее, чем циклами?
Я тебе на любое некомпакное решение на LINQ приведу еще более некомпактное решение на циклах Я, правда, не совсем понимаю как компактность кода определяет принадлежность чего либо только к РСУБД... Если циклы компактней LINQ, то LINQ только для РСУБД, а если наоборот — то циклы только для РСУБД?
IT>>[...] вести разговор в продуктивном ключе [...]
ГВ>Двумя руками "за". Show me your code, ok?
Ну так и начал бы с собственного решения на циклах?
Для твоей первой задачи.
Пускай у нас есть структура R:
public struct R
{
public TimeSpan time;
public int value;
public bool mark;
}
И пускай у нас есть некий генератор входных данных generator, который кидает событие,когда пришло новое значение R.
Тогда можно подписатья на него как-то так:
var ro = Observable.FromEvent<REventArgs>(g, "R_arrived_event").Select(ea => ea.R);
Наколенное решение задачи будуте что-то вроде такого (я заменил везде Seconds на Milliseconds, чтобы не состариться пока отлаживаю ):
List<R> buffer = new List<R> { new R() };
ro.Do(r =>
{
buffer.RemoveAll(rr => r.time - rr.time > TimeSpan.FromMilliseconds(1800));
buffer.Add(r);
})
.Where(r => r.mark)
.Subscribe(r => Console.WriteLine("mark = true at {0}, first within 1800 ms: {1}", r.time.TotalMilliseconds,
buffer[0].time.TotalMilliseconds));
P.S. Так как как у меня под рукой нету генератора , то использовал твои данные для проверки (это если кто-то еще захочет проверить), добавив в конец еще одно контрольное число с mark=true для пущей уверенности:
var rdata = new[]
{
new R{mark = false,time = TimeSpan.FromMilliseconds(840), value = rnd.Next()},
new R{mark = false,time = TimeSpan.FromMilliseconds(928), value = rnd.Next()},
new R{mark = false,time = TimeSpan.FromMilliseconds(1100), value = rnd.Next()},
new R{mark = false,time = TimeSpan.FromMilliseconds(1657), value = rnd.Next()},
new R{mark = false,time = TimeSpan.FromMilliseconds(1799), value = rnd.Next()},
new R{mark = false,time = TimeSpan.FromMilliseconds(1948), value = rnd.Next()},
new R{mark = true,time = TimeSpan.FromMilliseconds(3332), value = rnd.Next()},
new R{mark = true, time = TimeSpan.FromMilliseconds(3900), value=rnd.Next()}
};
var ro = Observable.Generate(0, // inital state
index => index < rdata.Length, // continue
index => rdata[index], // next value
index =>
{
if (index == 0)
return rdata[0].time;
if (index == rdata.Length)
return TimeSpan.FromMilliseconds(0);
return rdata[index].time - rdata[index - 1].time;
}, // waitInterval
index => index + 1 // iterate
);
P.P.S. Втотрую задачу не стал решать, ибо облом время тратить — тестовых входных данных колбасить еще больше, а решение еще проще. Грубо говоря, там будует что-то вроде stream1.WaitUntil(stream2.Where(st2item => st2item.mark)).Until(Observable.Interval(TimeSpan.FromHours(1)))
Luck in life always exists in the form of an abstract class that cannot be instantiated directly and needs to be inherited by hard work and dedication.
Здравствуйте, Lloyd, Вы писали:
G>>"Изменяемое состояние" обычно связывают с отсутствием "чистоты". Связь самая прямая.
L>Опять какая-то путаница. Понятно, что для мутабельнсости, нужно состояние, т.к. если нет состояния, то и менять нечего. Но у тебя вывод-то дурой: состояния нет, т.к. нет мутабельных операций. Причина со следствие переставлены. G>>Я не понимаю, что ты в данном случае называешь глобальным состоянием, может быть пояснишь? L>Поясню: массив, из кторого ты читаешь.
У меня никакой "путаницы" нет, я пользуюсь общепринятой терминологией среди функциональщиков. Объясняю.
Первое. Массив доступен глобально, но "состоянием" он не является, именно потому, что он иммутебельный.
"Протягивание состояния" — термин, обозначающий общую технику избавления от явного состояния, состоящую в передаче и транформации данных через последовательность вызовов, передаваемых и возвращаемых отдельным параметром через все вызовы.
Простейший пример — у меня есть цикл, который суммирует массив во внутренней мутабельной переменной. Она — является состоянием. Как мне от него избавиться? Преобразовать цикл в рекурсию, и передовать значение текущей суммы через параметр функции. Эта переменная — и есть "протянутое состояние".
Протяжку состояния можно сделать и неявной, при помощи монад, например, что не меняет сути дела.
Но в моем решении нет "протягивания состояния". Ни явного, ни скрытого. Массив не модифицируется, и не "протягивается" через вызовы. Соответственно, он не является никаким состоянием, ни явным, ни "протянутым".
L>Есть, более того, он (Zip) был использован в моем примере.
Вот и отлично.
G>>Если да, то ты можешь сгенерировать последовательность индексов, и использовать так, как используют ключи в реляционной БД. Клеишь эти индексы к значениям а, чтобы парами шли. Дальше — джойнами работаешь, схожими приемами, как в SQL. Вообще я затрудняюсь это более понятно объяснить.
L>Опять не понимаю. Можно кодом?
Можно конечно. Чуть позже.
G>>Не слишком эффективно, но сработать должно.
G>>Что касается построения первых двух множеств — там все еще проще, потому как известна "глубина просмотра" назад на каждой итерации, и она постоянна. Берешь исходный массив, сдвигаешь его, клеишь с исходным, и вперед.
L>Это и будет "простаскиванием" состояния.
Нет, как раз именно это и не является протаскиванием состояния. Протянутое состояние невозможно выразить в терминах comprehensions.
Вот если я вместо цикла напишу, например, какой-нибудь foldl, и буду транформировать массив на каждом шаге, передавая его изменения с одной итерации на другую — то это будет оно. Протянутое состояние.