Здравствуйте, IT, Вы писали:
IT>Громоздко и некрасиво, особенно, если нужны вложенные функции.
Всё ясно. Да, тут действительно с Linq не понятно, как поступать — разве что, заворачивать запрос в функцию и вызывать эту же функцию из запроса. В принципе, получается то же самое "протягивание" состояния. Может, кстати, lomeo прав насчёт отображения, сохраняющего структуру...
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Lloyd, Вы писали:
>>Первое. Массив доступен глобально, но "состоянием" он не является, именно потому, что он иммутебельный.
L>Почему ты ставишь знак равенства м/у состоянием и мутабельностью? Это разные вещи.
... L>Состояние != мутабельнсости.
Я считаю, что исчерпывающим образом объяснил, что я имею в виду, и ты при желании можешь понять, что я говорю. Спорить с тобой на эту тему я не собираюсь, считай как хочешь, это твое дело.
G>>Нет, как раз именно это и не является протаскиванием состояния. Протянутое состояние невозможно выразить в терминах comprehensions.
L>Расшифруй.
"Протягивание состояния" — термин, обозначающий общую технику избавления от явного состояния, состоящую в передаче и транформации данных через последовательность вызовов, передаваемых и возвращаемых отдельным параметром через все вызовы.
Простейший пример — у меня есть цикл, который суммирует массив во внутренней мутабельной переменной. Она — является состоянием. Как мне от него избавиться? Преобразовать цикл в рекурсию, и передовать значение текущей суммы через параметр функции. Эта переменная — и есть "протянутое состояние".
[ map( x ) | x <- y, filter( x ) ]
При использовании функций map и filter у тебя нет возможности "протянуть" состояние от вызова к вызову, так как ты не можешь передать его через параметр функции ни в map, ни в reduce. Ты можешь его передать только явно, через мутабельную переменную, что будет явным состоянием. А неизменяемые данные состоянием не являются.
Одним из следствий отсутствия изменяемого состояния является "прозрачность по ссылкам" — независимость результата от порядка вычислений.
G>>Вот если я вместо цикла напишу, например, какой-нибудь foldl, и буду транформировать массив на каждом шаге, передавая его изменения с одной итерации на другую — то это будет оно. Протянутое состояние.
L>Ты именно так и предлагаешь по сути делать. Только это протягивание будет не на этапе fold-а, а переде ним, там где ьы собрался делать склейку. В fold придет уже придет последовательность с нужными данными, но сформированы они как раз будет все через то же протягивание.
Нет, я предлагаю делать совершенно не так. В своем решении я не пользуюсь foldl. Если говорить в терминах функций, оно целиком основано на комбинации map, filter, и функций-генераторах числовых последовательностей. Ни то, ни другое не протягивает состояния.
Здравствуйте, Gaperton, Вы писали:
L>>Почему ты ставишь знак равенства м/у состоянием и мутабельностью? Это разные вещи. G>... L>>Состояние != мутабельнсости.
G>Я считаю, что исчерпывающим образом объяснил, что я имею в виду, и ты при желании можешь понять, что я говорю. Спорить с тобой на эту тему я не собираюсь, считай как хочешь, это твое дело.
Я вроде и не спорю. Я просто пытаюсь понять, твою логику, понять почему ты ставишь занк равенства между этими понятиями. Но если, не хочешь, не буду настаивать.
G>>>Нет, как раз именно это и не является протаскиванием состояния. Протянутое состояние невозможно выразить в терминах comprehensions.
L>>Расшифруй.
G>
G>"Протягивание состояния" — термин, обозначающий общую технику избавления от явного состояния, состоящую в передаче и транформации данных через последовательность вызовов, передаваемых и возвращаемых отдельным параметром через все вызовы.
G>Простейший пример — у меня есть цикл, который суммирует массив во внутренней мутабельной переменной. Она — является состоянием. Как мне от него избавиться? Преобразовать цикл в рекурсию, и передовать значение текущей суммы через параметр функции. Эта переменная — и есть "протянутое состояние".
Ты избавлялся от состояний и в результате получил "протянутое состояние". Я правильно понимаю, что "протянутое состояние" для тебя состоянием не является?
G>[ map( x ) | x <- y, filter( x ) ]
G>При использовании функций map и filter у тебя нет возможности "протянуть" состояние от вызова к вызову, так как ты не можешь передать его через параметр функции ни в map, ни в reduce. Ты можешь его передать только явно, через мутабельную переменную, что будет явным состоянием. А неизменяемые данные состоянием не являются.
Я приводил пример с методом AttachState. В нем нет изменяемых данных, но есть нечто (у меня оно было названо state), что передается от вызова к вызову и при этом от вызова к вызову меняется. Считаешь ли ты это нечто состоянием или нет?
G>>>Вот если я вместо цикла напишу, например, какой-нибудь foldl, и буду транформировать массив на каждом шаге, передавая его изменения с одной итерации на другую — то это будет оно. Протянутое состояние.
L>>Ты именно так и предлагаешь по сути делать. Только это протягивание будет не на этапе fold-а, а переде ним, там где ьы собрался делать склейку. В fold придет уже придет последовательность с нужными данными, но сформированы они как раз будет все через то же протягивание.
G>Нет, я предлагаю делать совершенно не так. В своем решении я не пользуюсь foldl. Если говорить в терминах функций, оно целиком основано на комбинации map, filter, и функций-генераторах числовых последовательностей. Ни то, ни другое не протягивает состояния.
Ок. С map и filter понятно. Осталось понять что ты называешь функциями-генераторами. Можно привести пример?
Здравствуйте, Lloyd, Вы писали:
L> В нем нет изменяемых данных, но есть нечто, что [...] меняется.
8-0 Это как?
state у тебя меняется явно в определении AttachState. А то, что у тебя при использовании AttachState, так это аргумент и результат функции, они да — неизменны. Например, item в Select тоже меняется
Насчёт состояние == мутабельность, это, конечно, не совсем так, но обычно, когда говорят "состояние" подразумевают именно изменяемое состояние. Обычно мало смысла говорить о неизменяемом состоянии:
const = 5
Глобальная переменная const — неизменяемая. Состояние или нет? Если да, то состояние — это что угодно, если нет, то под глобальным состоянием следует понимать именно изменяемое состояние. Как то так.
Здравствуйте, Lloyd, Вы писали:
G>>Я считаю, что исчерпывающим образом объяснил, что я имею в виду, и ты при желании можешь понять, что я говорю. Спорить с тобой на эту тему я не собираюсь, считай как хочешь, это твое дело.
L>Я вроде и не спорю.
Если не считать, что ты несколько раз в разных вариантах повторил, что "состояние != мутабельнсости", без каких-либо пояснений, то да — ты не споришь.
L>Я просто пытаюсь понять, твою логику, понять почему ты ставишь занк равенства между этими понятиями. Но если, не хочешь, не буду настаивать.
Ну, я объяснил как мог.
G>>>>Нет, как раз именно это и не является протаскиванием состояния. Протянутое состояние невозможно выразить в терминах comprehensions.
L>>>Расшифруй.
G>>
G>>"Протягивание состояния" — термин, обозначающий общую технику избавления от явного состояния, состоящую в передаче и транформации данных через последовательность вызовов, передаваемых и возвращаемых отдельным параметром через все вызовы.
G>>Простейший пример — у меня есть цикл, который суммирует массив во внутренней мутабельной переменной. Она — является состоянием. Как мне от него избавиться? Преобразовать цикл в рекурсию, и передовать значение текущей суммы через параметр функции. Эта переменная — и есть "протянутое состояние".
L>Ты избавлялся от состояний и в результате получил "протянутое состояние". Я правильно понимаю, что "протянутое состояние" для тебя состоянием не является?
Да, "протянутое состояние" вполне можно назвать состоянием.
G>>[ map( x ) | x <- y, filter( x ) ]
G>>При использовании функций map и filter у тебя нет возможности "протянуть" состояние от вызова к вызову, так как ты не можешь передать его через параметр функции ни в map, ни в reduce. Ты можешь его передать только явно, через мутабельную переменную, что будет явным состоянием. А неизменяемые данные состоянием не являются.
L>Я приводил пример с методом AttachState. В нем нет изменяемых данных, но есть нечто (у меня оно было названо state), что передается от вызова к вызову и при этом от вызова к вызову меняется. Считаешь ли ты это нечто состоянием или нет?
В принципе можно, с некоторой натяжкой. Ты можешь назвать его состоянием, с целью подчеркнуть, что оно "протягивается". И люди тебя в целом поймут.
G>>>>Вот если я вместо цикла напишу, например, какой-нибудь foldl, и буду транформировать массив на каждом шаге, передавая его изменения с одной итерации на другую — то это будет оно. Протянутое состояние.
L>>>Ты именно так и предлагаешь по сути делать. Только это протягивание будет не на этапе fold-а, а переде ним, там где ьы собрался делать склейку. В fold придет уже придет последовательность с нужными данными, но сформированы они как раз будет все через то же протягивание.
G>>Нет, я предлагаю делать совершенно не так. В своем решении я не пользуюсь foldl. Если говорить в терминах функций, оно целиком основано на комбинации map, filter, и функций-генераторах числовых последовательностей. Ни то, ни другое не протягивает состояния.
L>Ок. С map и filter понятно. Осталось понять что ты называешь функциями-генераторами. Можно привести пример?
Конечно. Генератором в comprehensions являются выражения вида (паттерн) <- (выражение, возвращающее множество). К примеру, n <- seq( 1, n, 2) — это генератор.
Функцией-генератором я в данном случае назвал seq( startIdx, endIdx, step ), которая генерирует последовательность индексов начиная со startIdx, не более чем endIdx, с шагом step. В Эрланге этому соответствует именно вызов функции. В Хаскеле/Clean может быть записан как список, что-то вроде [1..n], а при преобразовании comprehensions в циклы — эта штука преобразуется тупо в заголовок цикла for.
Здравствуйте, Gaperton, Вы писали:
G>Я имею в виду не фичи как таковые, а демонстрацию фичи на проблеме, где ее применение действительно мотивированно, и это не вызывает никаких сомнений. Это, естественно сделать гораздо тяжелее, чем рассуждать о тонкостях самой технологии. Ибо речь по сути не об ее устройстве, а об умении ее по делу применять.
IT>Без линка пришлось бы наворачивать циклы и состояния. Это я к вопросу о незначительности решения. И таких упражнений в день у меня по сто штук без всяких баз данных.
Положа руку на сердце, деле класс-расширение Linq.Enumerable к самому Linq постольку-поскольку относится, правильнее было его в какие-нить System.Collections закинуть. А вот аналогичный Linq.Queryable — ему в линке самое место, но ты его в примере не пользуешь.
Здравствуйте, lomeo, Вы писали:
L>> В нем нет изменяемых данных, но есть нечто, что [...] меняется.
L>8-0 Это как?
L>state у тебя меняется явно в определении AttachState. А то, что у тебя при использовании AttachState, так это аргумент и результат функции, они да — неизменны. Например, item в Select тоже меняется
Нет, для внешнего наблюдателя каждый раз порождается новое состояние на основе старого. Изменения переменных нет.
L>Насчёт состояние == мутабельность, это, конечно, не совсем так, но обычно, когда говорят "состояние" подразумевают именно изменяемое состояние. Обычно мало смысла говорить о неизменяемом состоянии:
Почему?
L>
L>const = 5
L>
L>Глобальная переменная const — неизменяемая. Состояние или нет? Если да, то состояние — это что угодно, если нет, то под глобальным состоянием следует понимать именно изменяемое состояние. Как то так.
Здравствуйте, Gaperton, Вы писали:
G>>>>>Нет, как раз именно это и не является протаскиванием состояния. Протянутое состояние невозможно выразить в терминах comprehensions.
L>>>>Расшифруй.
G>>>
G>>>"Протягивание состояния" — термин, обозначающий общую технику избавления от явного состояния, состоящую в передаче и транформации данных через последовательность вызовов, передаваемых и возвращаемых отдельным параметром через все вызовы.
G>>>Простейший пример — у меня есть цикл, который суммирует массив во внутренней мутабельной переменной. Она — является состоянием. Как мне от него избавиться? Преобразовать цикл в рекурсию, и передовать значение текущей суммы через параметр функции. Эта переменная — и есть "протянутое состояние".
L>>Ты избавлялся от состояний и в результате получил "протянутое состояние". Я правильно понимаю, что "протянутое состояние" для тебя состоянием не является?
G>Да, "протянутое состояние" вполне можно назвать состоянием.
Как же так, мы избавлялись от состояния, но оно все равно осталось. К чему было прилагать усилия?
G>>>[ map( x ) | x <- y, filter( x ) ]
G>>>При использовании функций map и filter у тебя нет возможности "протянуть" состояние от вызова к вызову, так как ты не можешь передать его через параметр функции ни в map, ни в reduce. Ты можешь его передать только явно, через мутабельную переменную, что будет явным состоянием. А неизменяемые данные состоянием не являются.
L>>Я приводил пример с методом AttachState. В нем нет изменяемых данных, но есть нечто (у меня оно было названо state), что передается от вызова к вызову и при этом от вызова к вызову меняется. Считаешь ли ты это нечто состоянием или нет?
G>В принципе можно, с некоторой натяжкой. Ты можешь назвать его состоянием, с целью подчеркнуть, что оно "протягивается". И люди тебя в целом поймут.
Но оно же не меняется. Просто на основе старого значения вячисляется новое. Разве не получается, что есть состояние и при этом оно неизменно.
G>>>>>Вот если я вместо цикла напишу, например, какой-нибудь foldl, и буду транформировать массив на каждом шаге, передавая его изменения с одной итерации на другую — то это будет оно. Протянутое состояние.
L>>>>Ты именно так и предлагаешь по сути делать. Только это протягивание будет не на этапе fold-а, а переде ним, там где ьы собрался делать склейку. В fold придет уже придет последовательность с нужными данными, но сформированы они как раз будет все через то же протягивание.
G>>>Нет, я предлагаю делать совершенно не так. В своем решении я не пользуюсь foldl. Если говорить в терминах функций, оно целиком основано на комбинации map, filter, и функций-генераторах числовых последовательностей. Ни то, ни другое не протягивает состояния.
L>>Ок. С map и filter понятно. Осталось понять что ты называешь функциями-генераторами. Можно привести пример?
G>Конечно. Генератором в comprehensions являются выражения вида (паттерн) <- (выражение, возвращающее множество). К примеру, n <- seq( 1, n, 2) — это генератор.
G>Функцией-генератором я в данном случае назвал seq( startIdx, endIdx, step ), которая генерирует последовательность индексов начиная со startIdx, не более чем endIdx, с шагом step. В Эрланге этому соответствует именно вызов функции. В Хаскеле/Clean может быть записан как список, что-то вроде [1..n], а при преобразовании comprehensions в циклы — эта штука преобразуется тупо в заголовок цикла for.
Тогда непонятно как ты будешь "транформировать массив", если ни одна из перечиленных функций не меняет состояния. Как в итоге массив окажется странсформированным?
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Как это вяжется с тем, что Linq, мол, предназначен для любых источников данных? Получается, что только для тех, которые сводятся к РСУБД-like.
Не к реляционным, а к спискам. Просто спискам. Просто таблицы в РСБУД — это тоже списки кортежей. Вот и все.
Все что может быть представлено в виде списка может быть обработано линком.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Lloyd, Вы писали:
L>Нет, для внешнего наблюдателя каждый раз порождается новое состояние на основе старого. Изменения переменных нет.
Это уже дело внешнего наблюдателя — интерпретировать. Как именно получен новый аргумент — на основе старого, не на основе — его не касается.
L>>Глобальная переменная const — неизменяемая. Состояние или нет? Если да, то состояние — это что угодно, если нет, то под глобальным состоянием следует понимать именно изменяемое состояние. Как то так. L>Состояние, конечно.
Хорошо. У нас есть состояние — все привязки в нашей программе, например, связи фунции с их именами. Какие рассуждения можно вести в терминах понятия "состояние" о программе, учитывая, что оно, а следовательно и поведение программы, неизменно? Какие рассуждения можно получить из понятия "Вселенная" в физике, а не в философии? Т.е. какие полезные рассуждения могут быть проведены?
Здравствуйте, VladD2, Вы писали:
VD>Не к реляционным, а к спискам. Просто спискам. Просто таблицы в РСБУД — это тоже списки кортежей. Вот и все. VD>Все что может быть представлено в виде списка может быть обработано линком.
А LINQ работает с деревом?
Если да — from obj in tree select obj.name — вернёт дерево имён или просто список?
Здравствуйте, IT, Вы писали:
IT>В данном случае простота напрямую зависит от уровня подготовки. Не секрет, что один и тот же код для одного человека может быть простым, а для другого сложным. Это как раз тот самый случай. Вот тут
я давал пример одного и того же алгоритма на Linq и в императивном стиле. Разница очевидна. ФП привносит в код декларативность, а значит простоту и гибкость. Но всё это требует определённой подготовки.
А совмещение императивного и функционального подхода читается еще проще:
static Dictionary<string, List<Percent>> CalcPercents2(
ProvidingVolume[] providingVolumes, ReceivingVolume[] receivingVolumes)
{
Dictionary<string, List<ProvidingVolume>> providingGroup = new Dictionary<string, List<ProvidingVolume>>();
foreach (ProvidingVolume pv in providingVolumes)
providingGroup.GetOrCreateValue(pv.Provider).Add(pv);
Dictionary<string, List<ReceivingVolume>> receivingGroup = new Dictionary<string, List<ReceivingVolume>>();
foreach (ReceivingVolume rv in receivingVolumes)
receivingGroup.GetOrCreateValue(rv.Account).Add(rv);
Dictionary<string, double> receivingSum = new Dictionary<string, double>();
foreach (List<ReceivingVolume> list in receivingGroup.Values)
receivingSum[list[0].Account] = list.Sum(delegate(ReceivingVolume rv) { return rv.Volume; });
Dictionary<string, List<Percent>> percents = new Dictionary<string, List<Percent>>();
foreach (List<ProvidingVolume> list in providingGroup.Values)
{
double sum = list.Sum(delegate(ProvidingVolume pv) { return pv.Volume; });
foreach (ProvidingVolume pv in list)
{
List<ReceivingVolume> rv = receivingGroup.FindObject(pv.Account);
if (rv != null)
{
foreach (ReceivingVolume r in rv)
{
Percent percent = new Percent();
percent.Provider = pv.Provider;
percent.Receiver = r.Receiver;
percent.Account = pv.Account;
percent.Value = pv.Volume / sum * r.Volume / receivingSum[r.Account];
percents.GetOrCreateValue(percent.Provider).Add(percent);
}
}
}
}
return percents;
}
Т.к. этот код сравним по лаконичности с чисто функциональным, но при чтении и при отладке элементарно бьется по шагам, а чисто функциональный код приходиться пытаться понять весь целиком, что на порядок сложнее.
static Dictionary<string, List<Percent>> CalcPercents(
ProvidingVolume[] providingVolumes, ReceivingVolume[] receivingVolumes)
{
var percents =
from percent in
from prov in
from p in providingVolumes
group p by p.Provider into pg
let total = pg.Sum(v => v.Volume)
from pp in pg
select new { pp, total }
join rec in
from r in receivingVolumes
group r by r.Account into rg
let total = rg.Sum(v => v.Volume)
from rr in rg
select new { rr, total }
on prov.pp.Account equals rec.rr.Account
select new Percent
{
Provider = prov.pp.Provider,
Receiver = rec.rr.Receiver,
Account = prov.pp.Account,
Value = prov.pp.Volume / prov.total * rec.rr.Volume / rec.total
}
group percent by percent.Provider;
return percents.ToDictionary(p => p.Key, p => p.ToList());
}
Re[22]: Для увеличения лаконичности еще var использовать
Здравствуйте, IT, Вы писали:
IT>В данном случае простота напрямую зависит от уровня подготовки. Не секрет, что один и тот же код для одного человека может быть простым, а для другого сложным. Это как раз тот самый случай. Вот тут
я давал пример одного и того же алгоритма на Linq и в императивном стиле. Разница очевидна. ФП привносит в код декларативность, а значит простоту и гибкость. Но всё это требует определённой подготовки.
Ежели сильно нужна лаконичность, то можно все переменные на var заменить, хотя читабельность это несколько ухудшит (за исключением использования var при создании Dictionary).
Здравствуйте, lomeo, Вы писали:
L>Здравствуйте, VladD2, Вы писали:
VD>>Не к реляционным, а к спискам. Просто спискам. Просто таблицы в РСБУД — это тоже списки кортежей. Вот и все. VD>>Все что может быть представлено в виде списка может быть обработано линком.
L>А LINQ работает с деревом? L>Если да — from obj in tree select obj.name — вернёт дерево имён или просто список?
И да и нет. Ответ зависит от наличия соответствующей реализации Query expression pattern-а. В дотнете идет реализация этого паттерна для всех типов, которые реализуют IEnumerable<T>. Но ничто не мешает обеспечить собственную реализацию для ITree<T>. Тогда from obj in tree select obj.name вытащит имена из дерева объектов и вернет дерево имен.
Здесь можно увидеть примеры других реализаций query expression pattern-a.
Здравствуйте, samius, Вы писали:
L>>Если да — from obj in tree select obj.name — вернёт дерево имён или просто список? S>И да и нет. Ответ зависит от наличия соответствующей реализации Query expression pattern-а. В дотнете идет реализация этого паттерна для всех типов, которые реализуют IEnumerable<T>. Но ничто не мешает обеспечить собственную реализацию для ITree<T>. Тогда from obj in tree select obj.name вытащит имена из дерева объектов и вернет дерево имен.
Странно, я предполагал, что типы функций у LINQ такие:
public static M<B> SelectMany<M, A, B>(this M<A> m, Func<A, M<B>> k)
Здравствуйте, VladD2, Вы писали:
ГВ>>Как это вяжется с тем, что Linq, мол, предназначен для любых источников данных? Получается, что только для тех, которые сводятся к РСУБД-like. VD>Не к реляционным, а к спискам. Просто спискам. Просто таблицы в РСБУД — это тоже списки кортежей. Вот и все. VD>Все что может быть представлено в виде списка может быть обработано линком.
Интересная мысль, но не совсем так. Список всё же подразумевает отношение порядка "последующий-предыдущий", или хотя бы "голова-хвост". В случае с LINQ ключевая концепция другая — итератор. Однонаправленный, заметь.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Здравствуйте, VladD2, Вы писали:
ГВ>>>Как это вяжется с тем, что Linq, мол, предназначен для любых источников данных? Получается, что только для тех, которые сводятся к РСУБД-like. VD>>Не к реляционным, а к спискам. Просто спискам. Просто таблицы в РСБУД — это тоже списки кортежей. Вот и все. VD>>Все что может быть представлено в виде списка может быть обработано линком.
ГВ>Интересная мысль, но не совсем так. Список всё же подразумевает отношение порядка "последующий-предыдущий", или хотя бы "голова-хвост". В случае с LINQ ключевая концепция другая — итератор. Однонаправленный, заметь.
Не итератор, а генератор итераторов. Он полностью изоморфен функциональному определению списка в виде голова:хвост.
Здравствуйте, lomeo, Вы писали:
L>Здравствуйте, samius, Вы писали:
L>>>Если да — from obj in tree select obj.name — вернёт дерево имён или просто список? S>>И да и нет. Ответ зависит от наличия соответствующей реализации Query expression pattern-а. В дотнете идет реализация этого паттерна для всех типов, которые реализуют IEnumerable<T>. Но ничто не мешает обеспечить собственную реализацию для ITree<T>. Тогда from obj in tree select obj.name вытащит имена из дерева объектов и вернет дерево имен.
L>Странно, я предполагал, что типы функций у LINQ такие:
L>
L>public static M<B> SelectMany<M, A, B>(this M<A> m, Func<A, M<B>> k)
L>