А как вы пишете сложный код?
От: Sinix  
Дата: 10.03.11 04:14
Оценка: 19 (3)
Hello 2 all!

Чуть-чуть введения: бывший коллега попросил рассказать, как быть с кодом, про который сам толком не можешь сказать — что он должен делать и как именно.

Парой месяцев раньше сам бы развёл руками и послал бы читать 2ю главу из Framework Design Guidelines (FDG), но тут нежданно припёрло и мне самому пришлось написать гору кода именно в таких условиях Чтобы не терялось, запощу тут.

Зачастую проблема даже не в том, что не знаешь, как подступиться к решению, а в том, что не знаешь, как решить задачу _лучше_. Есть решение со средней асимптотикой O(log N), есть O(n..N^2). Можно писать императивщину, можно сразу замахнуться на чисто-ФП решение. Что лучше? Что будет лучше в перспективе? Какие будут грабли?

Когда я в очередной раз оказался в роли Буриданова осла, решил попробовать на практике рекомендацию из FDG:

Framework Design Principle
Frameworks must be designed starting from a set of usage scenarios and code samples implementing these scenarios.


Итак, во что же это вылилось на практике?


1. Подготовка

Понадобится как минимум 1 дополнительный проект — для написания собственно сценариев использования. Поскольку у меня в основных проектах выше крыши продакшн-кода, там особенно не разгуляешься. Пришлось создавать ещё один проект — для прототипов.

Раньше я бы запихнул прототип в один проект с тестами и потерял бы очень важный бонус: когда тесты живут отдельно, они почти стопроцентно имитирует реальные сценарии использования. Если у вас нет никакого доступа к internal-member-ам и прочим внутренностям и вам неудобно использовать прототип, то, очевидно, реальный код тоже не будет отличаться умом и сообразительностью хорошим юзабилити.

Опустим неприятный момент придумывания сценариев использования, их написание и создание компилируемого кода-заглушки в сборке прототипов — всё это подробно описано во 2й главе FDG и кучу раз пережёвано в каждой TDD-дискуссии. Отмечу только, что код сценариев использования фактически является кодом интеграционных тестов (_не_ юнит-тестов) и что было бы глупо этим не воспользоваться


2. Пишем прототип

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

У нас уже есть интеграционные тесты, они же — сценарии использования. Остаётся убедиться, что решение корректно в целом. Конечно, можно использовать TDD. Увы, у меня он хорошо работает только при стабильной внутренней архитектуре кода, когда уже известно, что и как тестировать. У нас сейчас полностью обратная ситуация: мы хоть как-то определились с тем, что должен делать код, а вот как именно решать задачу — пока неизвестно.

Поэтому лично я предпочитаю писать ассерты прямо по месту использования — полный аналог if-then-throw, которыми напичкан фреймворк. Вся разница в том, что вместо разнородного кода аля
if (index < 0 || index >= list.Count)
{
  throw new ArgumentOfRangeException("index", index, "Bla-bla-bla 1");
}
// ...
// Я тут слегка беру через край, но сложные ассерты действительно просто записать кучей способов.
if (!(i < 0) && !(index < list.Count)) 
{
  throw new ArgumentOfRangeException("Bla-bla-bla 2!");
}

мы имеем
CollectionsCode.ValidIndex(index, "index", list);


Часть ассертов будет публичным контрактом прототипа: проверять корректность передаваемых параметров, допустимость вызова метода в текущем состоянии и т.п. Но большая часть ассертов (чем сложнее код — тем больше ассертов) будет заменять юнит-тесты и будет проверять инварианты — условия, который никогда не должны нарушаться в идеальном мире в правильно написанном коде. Разумеется, такие дебаг-ассерты можно пометить [Conditional("DEBUG")] и не волноваться за производительность продакшн-кода.


3. Переписываем прототип.

Предположим, что вы повторили пункт 2 столько раз, что сдались даже тесты и вы получили рабочее решение. Теперь можно, наконец, дать волю инстинктам и рефакторить, рефакторить, рефакторить до получения красивого кода

К сожалению, я не настолько идеален, и выход этапа 2 представляет из себя адскую мешанину ad-hoc-хелперов и комментариев "TODO:" и "REFINE:". Зато в голове у меня наконец-то откладывается главное: как можно решить нашу задачу, и как будет выглядеть код, решающий эту задачу.

Поэтому я делаю 3 вещи:
1. Пишу тест производительности прототипа;
2. Переписываю прототип набело, сохраняя все ассерты;
3. Прогоняю на готовом коде сценарии использования и тесты производительности. Исправляю и повторяю до получения приемлемых результатов.

Закончим маленьким примером. Прототип:
      SortedSet<int> changedIndexes = new SortedSet<int>();
      int oldCount = count;
      int newCount = changes[undoToEditOrdinal].TrackingValuesCount;

      for (int i = undoFromEditOrdinal; i >= undoToEditOrdinal; i--)
      {
        SwapValuesAndRemoveIndexes(changes[i], newCount, changedIndexes);
      }
      currentChangeSetOrdinal = undoToEditOrdinal - 1;
      count = newCount;

переписанный код:
      ChangeSetRange<T> undoRange = GetUndoRange(undoFromEditKey, undoToEditKey);
      DebugAssertState(currentEditKey == undoFromEditKey.EditManager.CurrentEditKey);

      foreach (ChangeSet2<T> changeSet in undoRange)
      {
        changeSet.Undo(source);
      }
      undoRange.MoveTo(redoChanges, 0);

      currentEditKey = undoToEditKey.EditManager.PreviousEditKey(undoToEditKey);

Хоть результат и не блещет внутренней красотой — это не public api, и от него не требуется универсальность и переиспользуемость — но, по крайней мере, он отражает _намерения_ программиста куда лучше, чем предыдущий код.


Disclaimer-ы и ответы на незаданные вопросы

Во-первых, спасибо всем, кто всё ещё со мной

Во-вторых, основная цель поста — показать, как на практике пишется "сделай не-знаю-что"-код. Ключевое слово — на практике, т.е. всё вышеперечисленное работает как минимум у меня.

В-третьих, ни одна сверхздравая идея не будет работать без трезвого к ней отношения. Не надо писать прототип _всего_. Не надо слепо следовать написанному и обобщать до "у меня не работает — отстой" или "у нас сработало — мегакруто, будем использовать везде". Короче говоря, мозг в комплект не входит

Напоследок, пара заранее заготовленных отмазок:

1. А нельзя ли было написать сразу кусок кода #2 (опционально почеркав на бумажке) и не мучать клавиатуру, себя, окружающих и бедный интернет?
Нельзя. Загвоздка в том, что исходная задача выглядела классическим чёрным ящиком: известно, что на входе, известно, что на выходе, решай как тебе удобно, и нет, точно такую проблему тут раньше не решали. И всё, что известно о коде выше для public api — это
  editManager.Undo(fillDataEditKey);


2. Не пиши код, на который нет требований.
Я только за

3. Используй xDD (подставить нужное).
С радостью. Делитесь — как решаете такие задачи вы?

Комментарии, возражения и холивары — велкам
Re: А как вы пишете сложный код?
От: мыщъх США http://nezumi-lab.org
Дата: 10.03.11 04:37
Оценка: 20 (1) +2
Здравствуйте, Sinix, Вы писали:

S>Hello 2 all!


S>Первое, что надо держать в голове, когда пишешь сам прототип: "ты сейчас пишешь код на выброс".

S>Это значит — никакого рефакторинга для улучшения архитектуры, никаких оптимизаций, никаких универсальных всемогуторов.
S> Сейчас главное — написать код, который хоть как-то работает, убедиться, что он работает правильно,
я бы еще добавил -- прототип должен быть максимально гибким. в частности, сейчас я пишу высокоскоростной парсер, работающий с битыми входными данными by design. и пишу его... на 90% на регулярках. просто чтобы продемонстрировать принципиальную возможность работы с битыми данными, а так же заставить этот код работать, методично опробуя различные подходы. регулярки тормозят ужасно, но зато их легко модифицировать. а вот если реализовать эту же логику "руками" -- мы утратим гибкость.

если позволяют ресурсы, то прототип можно воздвигать на каком-то скриптовом языке (например, на питоне), потому как там реально быстро программировать. намного быстее, чем си. правда, прототип, действительно, будет на "выбор" и его придется переписывать заново на целевом языке.
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Re: А как вы пишете сложный код?
От: Alexéy Sudáchen Чили  
Дата: 10.03.11 08:21
Оценка: +2
Ну в общем целом всё правильно, только вот отношение к коду странное. Ну то есть когда как делать непонятно это нормально. Точнее даже непонятно не как вообще делать, а какой способ выбрать. Оно очень часто так бывает, но что делать — вообще-то должно быть понятно. Даже когда цель 'cделать красиво' =).

Если человек не знает что должен делать его код или не представляет вообще ни одного способа каким образом код может это делать — он зря к компутеру подошёл. Лучше пойти погулять и подумать.

Реально прототип нужен шоб выбрать лучший способ или проверить что такой способ вообще реализуем. Может быть вообще на другом языке. Очень хорошо писать прототипы на Python'е.

И про код на выброс — это вопрос опыта. К примеру, я прототипы пишу очень часто, но то что доказало жизнеспособность легко трасформируется в продакшин. Даже если прототип на питоне, а конечный код на C/C++ — прототип это не только проверка реализуемости, но и референс реализация для проверки более сложного (рабочего) кода. Переписывать же код 'на бело' и 'с нуля' на том же языке =) Это не для меня. Ну о-о-о-чень ленивый. Просто надо понимать итеративность процесса. Не обязательно писать говнокод, можно просто писать только то что нужно сейчас, но не закрывать возможность реализовать что-то большее. Опять же Python для этого просто идельно подходит. Хотя это уже вопрос интуиции и опыта — не все могут просто сесть и совершенно естественно написать код, который легко будет менять/развивать.

Вот вчера писал прототип одной забавной фиговины, результат как раз-таки можно применять в пику деятельности коллег мыщЬха =) Я его практически в таком виде и выложу (в рекламных целях). Переписывания не требует. Другое дело что то что проверялось будет реализовано несколько иначе, в другом окружении и даже немного для другой цели =)

Ну и да, не люблю я это модное слово реФАКторинг. Само слово как бы намекает на созвучный процесс в отрицательном его смысле. Есть эволюция кода, программы, дизайна. Она естественна и не нуждается в каких либо искуственных методиках и насильственном процессе. Всё должно идти от реальных задач и условий работы этого кода, а не от папирусов с заветами =)
Re[2]: А как вы пишете сложный код?
От: Sinix  
Дата: 10.03.11 09:14
Оценка:
Здравствуйте, Alexéy Sudáchen, Вы писали:

AS>Если человек не знает что должен делать его код или не представляет вообще ни одного способа каким образом код может это делать — он зря к компутеру подошёл. Лучше пойти погулять и подумать.


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

Самый простой пример — анализ скомпилированного кода на c#, нахождение сложных свойств (т.е с геттером/сеттером, отличным от return field|field = value) и выдача варнинга, если где-то в коде к полю обращаются в обход свойства. И, тут же парная задача — предупреждать, когда код использует простые свойства вместо того, чтобы обращаться к полям напрямую.

Даже если ограничиться полуофициальным Microsoft.CCI, задачу можно решить 2мя подходами — их визитором или анализом последовательности il-инструкций. Дальше — веселее:
— строить стековую state-машину или использовать RX (почти удалось)?
— как быть с багами (в визиторе есть специальные костыли, но кто ж знал?)?
— что делать, если придётся дополнить логику, например, пропускать проверку конструкторов или находить свойства — обёртки вокруг других свойств?
— что мы пропустили (например, индексаторы)?


Ещё недавний пример — http://www.rsdn.ru/forum/dotnet/4158148.aspx
Автор: Sinix
Дата: 15.02.11
. Если интересует, поищу описание этого квеста.

Наконец, в принципе нерешаемая "правильным" способом задача —
http://www.rsdn.ru/forum/dotnet/3775195.aspx
Автор: Sinix
Дата: 15.04.10

http://stackoverflow.com/questions/2651327/nested-multithread-operations-tracing
http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/e7cc41f0-9be5-4a3f-8fc9-5a3bfe6bd7f2/

Это только задачи, связанные непосредственно с используемыми инструментами. С бизнес-логикой всё ещё веселее.




AS>Реально прототип нужен шоб выбрать лучший способ или проверить что такой способ вообще реализуем. Может быть вообще на другом языке. Очень хорошо писать прототипы на Python'е.


Не, для реального кода нужен dogfooding — все потенциальные косяки должны вылавливаться, пока пишется прототип. Иначе какая от него польза?

AS>И про код на выброс — это вопрос опыта. К примеру, я прототипы пишу очень часто, но то что доказало жизнеспособность легко трасформируется в продакшин.


Легко, но смысл идеи с прототипированием в том, чтобы _намеренно_ не писать продакшн код и не заморачиваться с кучей вещей, которые обязательны для продакшна: читаемостью, именованием, следованием FDG, понятной архитектурой, расширяемостью, простотой в использовании etc. Иначе написание прототипа превращается в "а тут документируем, тут строим иерархию наследования, тут добавляем поддержку config-файла, тут пишем костыли к wpf. Блин, всё непонятно! Рефакторить!".

Нет уж, мухи отдельно, котлеты — отдельно.

Разумеется, иногда получается чистый код, но даже в этом случае я предпочитаю создать новый класс, неспеша и понятно обзывать member-ы, копипастить и править код маленькими кусочками.




AS>Всё должно идти от реальных задач и условий работы этого кода, а не от папирусов с заветами =)

Ув. XopoSHiy эту мысль сформулировали гораздо лучше. Вот тут
Автор: XopoSHiy
Дата: 14.04.10
.
Re[3]: А как вы пишете сложный код?
От: Alexéy Sudáchen Чили  
Дата: 10.03.11 12:21
Оценка: +2
Здравствуйте, Sinix, Вы писали:

S>Это чересчур радикально Я тоже так думал, до тех пор пока не столкнулся с задачей "на тебе условия, нужен такой-то результат, а как ты будешь делать и можно ли это сделать вообще — это науке неизвестно".


У меня _ВСЕ_ задачи такого рода =) Большую часть времени я думаю, читаю, ковыряю отладчик и пишу маленькие но хитрые скрипты на питоне. Давно уже понял — пока я не знаю что именно я собираюсь делать, код писать бессмысленно.

Вы кстати зря смешиваете 'правильно' и качество кода. 'Правильно' вообще не бывает =), а качество кода оно хоть и оценивается субьективно но реально и сильно влияет на работу с кодом. ('Обьектиные' критерии качества кода — это вибратор для манагеров =))

S>Это только задачи, связанные непосредственно с используемыми инструментами. С бизнес-логикой всё ещё веселее.


Ой, тока давайте не будем страшными словами ругаться. Ну право слово, без контекста это на столько же многозначно, на сколько бессмысленно.

S>Легко, но смысл идеи с прототипированием в том, чтобы _намеренно_ не писать продакшн код и не заморачиваться с кучей вещей, которые обязательны для продакшна: читаемостью, именованием, следованием FDG, понятной архитектурой, расширяемостью, простотой в использовании etc. Иначе написание прототипа превращается в "а тут документируем, тут строим иерархию наследования, тут добавляем поддержку config-файла, тут пишем костыли к wpf. Блин, всё непонятно! Рефакторить!".


Э... а почему бы не иметь привычку всегда писать читаемый код с нормальным именованием, понятной расширяемой архитектурой и не заморачиваться вещами, которые на данной конкретной итерации не нужны?! Как бы не совсем понятно зачем писать иначе. То есть по вашему если пишешь прототип, то это обязательно гвонокод?! Просто по тому что писать говнокод быстрее и легче. Да нифига подобного. Это иллюзия =) Кстати, коли вы так любите это слово, что таки в вашем понимании рефакторить?

Я повторюсь, но я действительно считаю что прототипы нужно писать для проверки работоспособности идеи. Собственно качество кода прототипа это критерий профессионализма программера, а не препятсвие которое мешает писать =) Так что прототип — это не говнокод на выброс, а просто быстро написанный код проверяющий конкретную идею. Зачем впадать в крайности мне совсем не понятно. Как для прототипа, так и для рабочего кода. ИМХО, но любой живой проект на каждой стадии своего развития, в каком-то смысле, прототип для следующей. И что на каждой стадии его переписывать с нуля?

AS>>Всё должно идти от реальных задач и условий работы этого кода, а не от папирусов с заветами =)

S>Ув. XopoSHiy эту мысль сформулировали гораздо лучше. Вот тут
Автор: XopoSHiy
Дата: 14.04.10
.


Ну да, хотя я немного о другом говорил =)))). Но можно и на примере GoF. GoF — это общеобразовательная литература. Не список рецептов, но терминология. Однако не зная внутренних механизмов дизайна невозможно понять почему шаблоны из GoF такие, какие они есть. Но эти механизмы по GoF особенно то и не понять. От народ и пишет декоратоы, фабрики и визитёры. Но _зачем_ они это делают — не понимают. Тут надо что-то типа книг/статей Боба Мартина читать.

Точно так же далеко не все любители рефакторить понимают зачем они это делают и что именно. Как следствие, сей рефакторинг превращается в утомительный процесс, который несовместим с прототипированием и применим только к продакшен коду. Что ИМХО есть бред сивой кобылы =)
Re: А как вы пишете сложный код?
От: WolfHound  
Дата: 10.03.11 12:32
Оценка: +1
Здравствуйте, Sinix, Вы писали:

Никак.
Если код получается сложным значит ты идешь не тем путем или используешь плохой инструмент.
Взять например ту задачу которую ты тут упомянул: editManager.Undo(fillDataEditKey);
Тебе говорили как свести решение к тривиальному но ты не послушал.
В результате тебе пришлось развести целую философию о том как писать сложный код.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[4]: А как вы пишете сложный код?
От: Sinix  
Дата: 10.03.11 13:49
Оценка:
Здравствуйте, Alexéy Sudáchen, Вы писали:

AS>Э... а почему бы не иметь привычку всегда писать читаемый код с нормальным именованием, понятной расширяемой архитектурой и не заморачиваться вещами, которые на данной конкретной итерации не нужны?!


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

AS>Как бы не совсем понятно зачем писать иначе. То есть по вашему если пишешь прототип, то это обязательно гвонокод?!


Где я про это говорил? И кстати, найдите хоть один "говнокод" с моей стороны. Не только в этой теме — вообще на форуме. Давайте менять стиль дискуссии, тут всё-таки философия, а не КСВ

Поясню, что именно мне не нравится.

Я описал условия и во введении, и в disclaimer-е. Специально для вас привёл реальные случаи, когда сложность реализации превосходит концептуальную сложность и бесполезно решать задачу без написания рабочего кода.

В таких условиях неизбежно придётся перебирать дерево вариантов, раз за разом возвращаясь к менее приоритетным решениям. Причём чем быстрее будет перебор, тем лучше. Поэтому в ходе набросков прототипа бессмысленно удалять временные хелперы, писать быстрый поиск вместо гарантированно рабочего перебора, и _специально_ писать абсолютно корректный код. Вы всё написанное можете выкинуть через 10 минут, а ещё через 20 — вернётесь к предыдущему варианту.

Вы говорите, что всё — фигня, сводите тему к слегка другой задаче ("прототипы нужно писать для проверки работоспособности идеи"), добавляете утверждения, которых я не делал:

Просто по тому что писать говнокод быстрее и легче. Да нифига подобного. Это иллюзия =)

и долбестно их опровергаете. Мне это не нравится, срач разводить не хочется, поэтому пока что закругляюсь. Как бы то ни было, спасибо за участие!

AS>Однако не зная внутренних механизмов дизайна невозможно понять почему шаблоны из GoF такие, какие они есть. Но эти механизмы по GoF особенно то и не понять...

+1
Re[2]: А как вы пишете сложный код?
От: Sinix  
Дата: 10.03.11 14:17
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Если код получается сложным значит ты идешь не тем путем или используешь плохой инструмент.

Зачастую задачи, которые приходится решать, вообще слишком сложны для более-менее адекватной формализации. Или вообще нет адекватных средств для их решения. Примеры приводил тут
Автор: Sinix
Дата: 10.03.11
.

WH>Взять например ту задачу которую ты тут упомянул: editManager.Undo(fillDataEditKey);

WH>Тебе говорили как свести решение к тривиальному но ты не послушал.

Это про переход к immutable и взятие снапшотов?
Отвечал уже тут
Автор: Sinix
Дата: 15.01.11
, тут
Автор: Sinix
Дата: 15.01.11
и тут
Автор: Sinix
Дата: 15.01.11
. Ответных ответов не увидел

Повторю самый простой юз-кейз: десктопное приложение, исходный список прибинден к нескольким контролам с фильтром и сортировкой, сотни отменяемых редактирований с одним-двумя изменениями за раз.

Во-первых, при таком подходе мы тратим в десятки раз больше памяти, чем надо.
Во-вторых, чтобы работал биндинг, список должен реализовать INotifyCollectionChanged. Для того, чтобы не обновлять всю коллекцию, нам потребуется динамически вычислять diff между снапшотами.

И на закуску — сценарий чуть посложнее: отслеживание изменений с последующим сохранением в базу. Разумеется, с топологической сортировкой всего графа — чтобы не нарушать constraints.

Поймите, бессмысленно писать фреймворк в ФП-style, если 100% пользовательского кода — императивщина.
Re[3]: А как вы пишете сложный код?
От: WolfHound  
Дата: 10.03.11 14:36
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Зачастую задачи, которые приходится решать, вообще слишком сложны для более-менее адекватной формализации. Или вообще нет адекватных средств для их решения. Примеры приводил тут
Автор: Sinix
Дата: 10.03.11
.

Ответ тут
Автор: hardcase
Дата: 09.03.11
и тут
Автор: Sinix
Дата: 09.03.11
.

S>Это про переход к immutable и взятие снапшотов?

S>Отвечал уже тут
Автор: Sinix
Дата: 15.01.11
, тут
Автор: Sinix
Дата: 15.01.11
и тут
Автор: Sinix
Дата: 15.01.11
. Ответных ответов не увидел

Ну конечно ты же все просто проигнорировал и мне надоело биться головой о каменную стену ради не пойми чего.

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

Можно подумать у тебя все на святом духе держится и история память не жрет.

S>Во-вторых, чтобы работал биндинг, список должен реализовать INotifyCollectionChanged. Для того, чтобы не обновлять всю коллекцию, нам потребуется динамически вычислять diff между снапшотами.

Нет ту никаких проблем. Тем более что дифф ты получишь бсплатно.

S>И на закуску — сценарий чуть посложнее: отслеживание изменений с последующим сохранением в базу. Разумеется, с топологической сортировкой всего графа — чтобы не нарушать constraints.

Тяжолый ОРМ? Это само по себе сильно зря. В не зависимости от подхода.

S>Поймите, бессмысленно писать фреймворк в ФП-style, если 100% пользовательского кода — императивщина.

А может императивщина не нужна? Ты об этом не думал?
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[4]: А как вы пишете сложный код?
От: Sinix  
Дата: 10.03.11 14:49
Оценка:
Здравствуйте, WolfHound, Вы писали:

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

WH>Можно подумать у тебя все на святом духе держится и история память не жрет.
Жрёт, но на порядок меньше — хранятся только изменённые элементы, а не весь список целиком. Вывод тестов могу найти в понедельник-вторник, но код пока что опубликовать не смогу

S>>Во-вторых, чтобы работал биндинг, список должен реализовать INotifyCollectionChanged. Для того, чтобы не обновлять всю коллекцию, нам потребуется динамически вычислять diff между снапшотами.

WH>Нет ту никаких проблем. Тем более что дифф ты получишь бсплатно.
Как? В состоянии один у нас [0,1,2,3,4], в состоянии 2 — [0,14,2,32,4]. Перебирать 2 списка и сравнивать попарно?
Кроме того, как быть с биндингами, с гридами и прочим UI? Там хош-не-хош надо реализовать IList+INotifyCollectionChanged и имитировать поведение изменяемых коллекций. То же самое относится к большей части существующего кода.

S>>И на закуску — сценарий чуть посложнее: отслеживание изменений с последующим сохранением в базу. Разумеется, с топологической сортировкой всего графа — чтобы не нарушать constraints.

WH>Тяжолый ОРМ? Это само по себе сильно зря. В не зависимости от подхода.
Не, скорее нечто ближе к датасету, но без его косяков.

S>>Поймите, бессмысленно писать фреймворк в ФП-style, если 100% пользовательского кода — императивщина.

WH>А может императивщина не нужна? Ты об этом не думал?
Думал, и (лично я) — не против. Осталось объяснить это всему мейнстриму.
Re[5]: А как вы пишете сложный код?
От: Alexéy Sudáchen Чили  
Дата: 10.03.11 15:26
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Я описал условия и во введении, и в disclaimer-е. Специально для вас привёл реальные случаи, когда сложность реализации превосходит концептуальную сложность и бесполезно решать задачу без написания рабочего кода.


Можно словами такой пример описать и желательно не представляющий из себя борьбу с инструментом? Я не пишу под .NET и чтобы понять проблему которую вы решаете мне сначала нужно разобраться в фреймворке и среде исполнения. Оно мне надо?! Я вообще с этим 'богатством' предпочитаю не связываться. FDG для меня вообще ничего не значащее сочитание букв.

S>Вы говорите, что всё — фигня, сводите тему к слегка другой задаче ("прототипы нужно писать для проверки работоспособности идеи"), добавляете утверждения, которых я не


Да это ФИГНЯ. Уж простите мой французкий.

Я выступаю против двух тезисов: 'прототип — это код на выброс' и 'можно что-то писать (прототипировать) на зная что должен делать код, который пишешь'. Первое признак излишка времени либо неумения писать код, второе — вообще бред. Не существует правильного и совершенного кода, любой код всего лишь прототип того чем он станет в слующей итерации. Вы же почему-то говорите о неком прототипе который выбрасывается (из за низкого качества код или несоответсвия стандартам или нучёта какой-то архитектуры), а проверенная идея пишется с нуля как совершенный код учитывающий всё на свете. А если не учитывающий, то чем он от прототипа то отличается? Живой код всегда пишется от состояния 'что-то работает' к состаянию 'делает всё что нужно с требуемыми характеристиками' и ни в одном состоянии не имеет заявки на совершенство.

ИМХО конечно, однако у меня действительно практически все задачи из разряда ХЗ как делать, так что я знаю о чём говорю.
Re: А как вы пишете сложный код?
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 10.03.11 16:16
Оценка: 10 (1) +1
Здравствуйте, Sinix, Вы писали:

S>Комментарии, возражения и холивары — велкам


А что такое сложный код? Мне кажется, что тут можно придумать очень много самых разных задач, для которых нужен свой подход

Для одного кода я зарылся в MATLAB и там пробовал разные варианты. Когда нашел приемлемый --- закодировал идею почти на чистом C. Во втором случае просто начал с разработки разных тестов, где задания формировались в отдельном файле. Потом опустился на уровень процедур и функций, прикрутил логирование каждого шага, анализатор этих логов и копался в этом.
Re[2]: А как вы пишете сложный код?
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 10.03.11 16:41
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Если код получается сложным значит ты идешь не тем путем или используешь плохой инструмент.

WH>Взять например ту задачу которую ты тут упомянул: editManager.Undo(fillDataEditKey);

А можно взять другую задачу? Например, написать программу, которая бы играла в го в силу про-дана на одном ядре с контролем 30'+30". Каким тут путем идти, чтобы код получился простым?
Re[4]: А как вы пишете сложный код?
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 10.03.11 16:43
Оценка: 1 (1)
Здравствуйте, Alexéy Sudáchen, Вы писали:

AS>У меня _ВСЕ_ задачи такого рода =) Большую часть времени я думаю, читаю, ковыряю отладчик и пишу маленькие но хитрые скрипты на питоне. Давно уже понял — пока я не знаю что именно я собираюсь делать, код писать бессмысленно.


У меня часто наоборот бывает. Начинаешь писать код, понимаешь, что собираешься сделать. А иначе можно до конца века видеть и размышлять, а всего в голове не удержишь
Re[2]: А как вы пишете сложный код?
От: Sinix  
Дата: 10.03.11 16:44
Оценка:
Здравствуйте, Mystic, Вы писали:

M>А что такое сложный код? Мне кажется, что тут можно придумать очень много самых разных задач, для которых нужен свой подход

Название топика не самое удачное, согласен
Re[3]: А как вы пишете сложный код?
От: WolfHound  
Дата: 10.03.11 17:10
Оценка:
Здравствуйте, Mystic, Вы писали:

M>А можно взять другую задачу? Например, написать программу, которая бы играла в го в силу про-дана на одном ядре с контролем 30'+30". Каким тут путем идти, чтобы код получился простым?

Напиши программу которая подбирает коллизию для SHA512 на одном ядре за пару секунд.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[5]: А как вы пишете сложный код?
От: WolfHound  
Дата: 10.03.11 17:10
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Жрёт, но на порядок меньше — хранятся только изменённые элементы, а не весь список целиком. Вывод тестов могу найти в понедельник-вторник, но код пока что опубликовать не смогу

Значит ты что-то не так делаешь.

S>Думал, и (лично я) — не против. Осталось объяснить это всему мейнстриму.

Короче бодаться с тобой в очередной раз мне лень.
Сойдемся на том что: Технологии менстрима заставляют писать неоправданно сложный код.
Я свой выбор сделал не в пользу этих технологий. Если комуто хочется жрать кактус то я отговоривать не буду. Мне лениво.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[4]: А как вы пишете сложный код?
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 10.03.11 17:20
Оценка: +2
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, Mystic, Вы писали:


M>>А можно взять другую задачу? Например, написать программу, которая бы играла в го в силу про-дана на одном ядре с контролем 30'+30". Каким тут путем идти, чтобы код получился простым?

WH>Напиши программу которая подбирает коллизию для SHA512 на одном ядре за пару секунд.

Ну я же не говорил, что

Если код получается сложным значит ты идешь не тем путем или используешь плохой инструмент.


Я вполне допускаю, что есть задачи, где от сложности кода уйти никуда нельзя, например, где сложность кода это прямое следствие сложности алгоритмов и самой задачи.
Re[2]: А как вы пишете сложный код?
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.03.11 19:04
Оценка: :))
Здравствуйте, мыщъх, Вы писали:

М>я бы еще добавил -- прототип должен быть максимально гибким. в частности, сейчас я пишу высокоскоростной парсер, работающий с битыми входными данными by design. и пишу его... на 90% на регулярках.


Жуть! Я бы в таком даже побоялся бы признаться. В прочем я бы таким и заниматься не стал бы.

М>просто чтобы продемонстрировать принципиальную возможность работы с битыми данными, а так же заставить этот код работать, методично опробуя различные подходы. регулярки тормозят ужасно, но зато их легко модифицировать. а вот если реализовать эту же логику "руками" -- мы утратим гибкость.


Руками? А почитать что-нить по теме парсеров не пробовал для начала? Не все задачи берутся методом замучивания.

Парсить регулярками — это из серии вырезать гланды через жопу автогеном.

М>если позволяют ресурсы, то прототип можно воздвигать на каком-то скриптовом языке (например, на питоне), потому как там реально быстро программировать. намного быстее, чем си. правда, прототип, действительно, будет на "выбор" и его придется переписывать заново на целевом языке.


Прототипы и делаются для того чтобы их выбрасывать (я правильно понял, что вместо "выбор" имелось в виду "выброс"?).
Так что это как раз не проблема. Проблема в том что в области парсинга ни регулярки, ни (тем более) скрипты не помощники.
И как раз "руками" тут получится наиболее гибко. Проблема в том, что при всех гибкости это будет и наиболее сложный вариант.

В общем, мой тебе совет. Возьми подходящий инструмент и сделай свой прототип. Подходящий интсрумент тут — это генератор парсеров. Я бы посоветовал бы взять наш Nemerle.Peg. Он грамматики любой сложности возьмет. Ну, а "битые входные данные" это ни что иное как более сложные грамматики. И PEG тут будет самым подходящим инструментом. В прочем, любой генератор парсеров будет лучше чем регулярки или ручное написание парсера.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: А как вы пишете сложный код?
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.03.11 19:09
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Это про переход к immutable и взятие снапшотов?

S>Отвечал уже тут
Автор: Sinix
Дата: 15.01.11
, тут
Автор: Sinix
Дата: 15.01.11
и тут
Автор: Sinix
Дата: 15.01.11
. Ответных ответов не увидел


Ткнул на ссылки и увидел вполне вменяемые ответы. Может не хотел увидеть?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: А как вы пишете сложный код?
От: Alexéy Sudáchen Чили  
Дата: 10.03.11 23:31
Оценка:
Здравствуйте, Mystic, Вы писали:

AS>>У меня _ВСЕ_ задачи такого рода =) Большую часть времени я думаю, читаю, ковыряю отладчик и пишу маленькие но хитрые скрипты на питоне. Давно уже понял — пока я не знаю что именно я собираюсь делать, код писать бессмысленно.

M>У меня часто наоборот бывает. Начинаешь писать код, понимаешь, что собираешься сделать. А иначе можно до конца века видеть и размышлять, а всего в голове не удержишь

И чего пишешь когда начинаешь, если не понимаешь чего собираешся делать?! ИМХО, не надо всё в голове держать, ты же не всё за раз пишешь, а по кусочкам. В голове только этот кусочек и общая (текущая) картина того что должно получится и где этот кусочек в ней находится. В моём понимании программирование чего либо сложнее helloworld в принципе есть циклическое и кусочное прототипирование желаемого. Собственно принципильное отличие между моей точкой зрения и позицией топик-стартера, прототипировать надо части живой софтины или отдельные минималистические кейсы, возможно вообще на другом языкеи и в другом окружении. И такой прототип вполне себе рабочая часть этого 'организма', требующая переработки только когда не справляется с меняющимеся требованиями.

Просто не надо пытаться написать совершенный код, который разве что кофе в постель не приносит =) Программы надо не 'строить' их надо 'выращивать' =)))

Однако, нельзя просто сесть и начать писать какие-то конструкции языка. Сначала надо создать в голове хоть какую-то модель решения задачи и только после этого попробовать её реализовать. Иначе это не программирование, а какое-то рифмоплётство получается.

Ну да, скоко-то лет тому назад я именно так и писал (от пальцев), но не от эффективности метода, а от пустой головы. Потом мне мозги вправили и через несколько лет я перистал заниматься таким творчеством. =)
Re[3]: А как вы пишете сложный код?
От: мыщъх США http://nezumi-lab.org
Дата: 11.03.11 00:02
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, мыщъх, Вы писали:


М>>я бы еще добавил -- прототип должен быть максимально гибким. в частности, сейчас я пишу высокоскоростной парсер, работающий с битыми входными данными by design. и пишу его... на 90% на регулярках.

VD>Жуть! Я бы в таком даже побоялся бы признаться. В прочем я бы таким и заниматься не стал бы.
в общем не такая уж и жуть. 73 мегабайта парсятся на питоне с регулярками за пару минут на тормозном ноуте. я не совсем двинутый, чтобы писать парсер _полностью_ на регулярках.

основная сложность задачи даже не распарсить битый скрипт (это как раз как два байта переслать), сложнее реконструировать (насколько это возможно) отсутствующий код. вот допустим, у вас есть нечто вида data = foo(a,b,c), причем foo не библиотечная функция и она физически отсутствует, но мы можем проанализировать происхождение аргументов и что делается с возвращаемым значением, автоматически заменяя функцию foo тем, что она предположительно делает.

вопрос: какие у нас шансы на удачу? стоит ли игра свеч? какие общие закономерности "побитости" прослеживаются во входных данных? теоритически на этот вопрос ответить невозможно, т.к. нужно смотреть реальный характер повреждений, а чем его смотреть? гигабайты данных ведь не перелопатишь руками...

поигравшись с разными моделями, пришел к выводу, что в 30% случаев все можно угадать тупым набором заранее сгенерированных правил. но 30% это неинтересно и потому играюсь дальше. в принципе, гадать можно достаточно долго (для начала в off-line). если скрипт выдаст осмысленный результат -- значит, мы угадали. ну а нет, так нет.


VD>Руками? А почитать что-нить по теме парсеров не пробовал для начала? Не все задачи берутся методом замучивания.

читаю книгу дракона (второе издание, ценую в сто баксов). а что вы еще можете порекомендовать? существующие инструменты крайне плохо подходят для решения данной задачи. ну вот например:

foo(val){a='olla-la-shit-happensf.read();f.close();return data;}.

понятно, что тут имеет место быть выпавший фрагмент. read() уже как бы наводит на мысли. а return data позволяет предположить, что это было data = f.read(); есть так же вероятность, что val это имя файла, которое нужно открыть.

сейчас я все это могу распарсить на регулярках, т.е. понять, что 'olla-la-shit-happens' это одно, а f.read это уже другое. именно f.read, а не happensf.read, т.к. дальше идет f.close.

ну вот чем такое парсить-то? что из готовых инструментов вы посоветуете? у меня токенизация делается по списку ключевых слов. в первом проходе это будет read, close, return. во втором проходе мы накладываем шаблон, специфичный для каждого ключевого слова и выделяем val, f, data. в третьем проходе мы с учетом знания о поведении каждого "токена" строим "смысловые" связи и зависимости по данным. val -> ???, f <-- open(???), read() -> ?resilt?, return data, ?result?.

на финальном проходе мы выдвигаем предположение о возможной связи val и open, а так же read и return. в данном случае у нас только одна комбинация подходящая по смыслу. и дальше мы смотрим кто вызываем foo, какие аргументы ей передают и как используют ее данные.


VD>Парсить регулярками — это из серии вырезать гланды через жопу автогеном.

это верно. но... пока остановился на таком варианте, т.к. он позволяет менять логику работы парсера на лету.


VD>В общем, мой тебе совет. Возьми подходящий инструмент и сделай свой прототип. Подходящий

VD>интсрумент тут — это генератор парсеров. Я бы посоветовал бы взять наш Nemerle.Peg. Он грамматики любой сложности возьмет.
у нас половина людей под никсами, вторая половина под маками. код им понимать необязательно, но он у них должен по крайней мере работать. я не в курсе как у вас с кросс-платформенностью. под линухом заведется?

и дело тут не в сложности грамматики. это JavaScript, VBS. под JS есть готовые парсеры/грамматики/whatever, но нету таких, которые бы отвечали условиям задачи (как в примере выше, который _очень_ типичный пример).

> Ну, а "битые входные данные" это ни что иное как более сложные грамматики. И PEG тут будет самым подходящим инструментом.

> В прочем, любой генератор парсеров будет лучше чем регулярки или ручное написание парсера.
подскажите, пожалуйста, а то я уже мозг сломал -- как какому-нибудь стандартному генератору парсеров объяснить, что строка может не закрываться кавычкой или начинаться не с кавычки (т.е. ello, world\n'; var a = 'AAA'; ). парсить "по науке" у меня не получается. ну не знаю я как описать такую грамматику, а потому сначала выделяю ключевые слова ("ключевые" в смысле словарные), а уже отталкиваясь от них бью текст на токены, но это разбивка предположительная, и там приходится искать лучшее соответствие. я стрю семантическое дерево (если я правильно его называю), в котором токены связаны во всех комбинациях, которые допускаются заранее заданными правилами и ищется самый длинный маршрут в дереве.
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Re[4]: А как вы пишете сложный код?
От: Sinix  
Дата: 11.03.11 01:12
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Ткнул на ссылки и увидел вполне вменяемые ответы. Может не хотел увидеть?

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

Если последовать совету ув. WolfHound, перейти к immutable спискам и хранить копии списков для undo-redo, как быть с биндингом, с вычислением изменений, с уведомлением об изменениях (они нужны как минимум для ограничений)?

Нам придётся имитировать поведение BindingList|ObservableCollection, и весь выигрыш от первоначальной идеи уйдёт на допиливание обёрток. Я не просто так, абстрактно говорю — я делал вполне рабочий прототип и делал замеры. В таком виде идея не взлетит.
Re[6]: А как вы пишете сложный код?
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 11.03.11 10:46
Оценка:
Здравствуйте, Alexéy Sudáchen, Вы писали:

AS>Здравствуйте, Mystic, Вы писали:


AS>>>У меня _ВСЕ_ задачи такого рода =) Большую часть времени я думаю, читаю, ковыряю отладчик и пишу маленькие но хитрые скрипты на питоне. Давно уже понял — пока я не знаю что именно я собираюсь делать, код писать бессмысленно.

M>>У меня часто наоборот бывает. Начинаешь писать код, понимаешь, что собираешься сделать. А иначе можно до конца века видеть и размышлять, а всего в голове не удержишь

AS>И чего пишешь когда начинаешь, если не понимаешь чего собираешся делать?!


А шо делать? Ждать, пока "понимание" спустится свыше?

AS>ИМХО, не надо всё в голове держать, ты же не всё за раз пишешь, а по кусочкам.


Не всегда выполнимо. Например, я разрабатываю алгоритм, который должен правильно работать для различных типовых задач (их около десятка). Всякое изменение алгоритма для одной типовой задачи затрагивает остальные девять. В ряде случаев я даже затрудняюсь, что надо вернуть. А есть еще куча случаев, о которых я вообще не подумал, их еще надо обнаружить. Для начала мне надо исследовать проблему, вот я и делаю шаг влево, шаг вправо. Пробую что-то писать и вижу: тут возникает такая вот нестыковка. Ага, надо придумать что-то другое...

Да, я пишу кусочками, но это не част общей мозаики, а скорее блуждание по лабиринту.
Re: А как вы пишете сложный код?
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.03.11 10:50
Оценка:
Здравствуйте, Sinix, Вы писали:

На мой взгляд есть всего один способ писать сложный код — продумать как и что делать, а потом сесть и написать.

Прототипирование решает всего одну проблему — устраняет неопределенность. Имея грамотно сделанный прототип можно отбросить гадания и выбрать правильный результат.

Но в любом случае для решения сложных задач намного важнее две вещи:
1. Продуманный план (который может видоизменяться в процессе разработки в виду появления новых данных).
2. Вол и упорство.

Очень советую почитать статью IT о видах сложностей. Ты явно не читал ее, или читал по диагонали. Если прочесть ее внимательно, то можно заметить, что твой вопрос сформулирован не верно. Сложности бывают разными. И их можно трансформировать. Так вот есть задачи сложность которых заключается в объемах. А есть те чья сложность заключается в интеллектуальной составляющей. И последний вид проблем не решить качественно без пополнения собственных знаний.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: А как вы пишете сложный код?
От: Sinix  
Дата: 11.03.11 11:17
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>На мой взгляд есть всего один способ писать сложный код — продумать как и что делать, а потом сесть и написать.

Ок, как быть, когда _детально_ придумать что и как делать на порядки сложнее/дороже по времени, чем просто написать код? Когда известна архитектура, известно, как (примерно) будет выглядеть API, но сам код можно написать несколькими способами?

Я ведь уже приводил кучу примеров, когда попытки решить задачу с наскока сваливались в перебор всего дерева вариантов. Дошли до аргументации в духе "а нафига мейнстрим, если в нём не работает ФП-подход?".

На конкретный вопрос (несколько раз ведь спрашивал) ни вы, ни WolfHound не ответили, предпочитая К.О. — утверждения

Но в любом случае для решения сложных задач намного важнее две вещи:
1. Продуманный план (который может видоизменяться в процессе разработки в виду появления новых данных).
2. Вол и упорство.

и лёгкие наезды аля

Очень советую почитать статью IT о видах сложностей. Ты явно не читал ее, или читал по диагонали.


Предлагаю закругляться.
Re[4]: А как вы пишете сложный код?
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.03.11 11:56
Оценка: 1 (1)
Здравствуйте, мыщъх, Вы писали:

М>...


Задача весьма странная. Интересно откуда у нее растут ноги? Что ломает эти скрипты?

VD>>Руками? А почитать что-нить по теме парсеров не пробовал для начала? Не все задачи берутся методом замучивания.

М>читаю книгу дракона (второе издание, ценую в сто баксов).

Это бесполезное занятие. Особенно если это русский вариант (перевод). Книга сильно устарела и не содержит передовых подходов.

М>а что вы еще можете порекомендовать?


Давай лучше на ты.

М>существующие инструменты крайне плохо подходят для решения данной задачи. ну вот например:


М>foo(val){a='olla-la-shit-happensf.read();f.close();return data;}.


М>понятно, что тут имеет место быть выпавший фрагмент. read() уже как бы наводит на мысли. а return data позволяет предположить, что это было data = f.read(); есть так же вероятность, что val это имя файла, которое нужно открыть.


М>сейчас я все это могу распарсить на регулярках, т.е. понять, что 'olla-la-shit-happens' это одно, а f.read это уже другое. именно f.read, а не happensf.read, т.к. дальше идет f.close.


В твое задаче просто напрашивается:
1. Применение PEG-а для распознавания битых последовательностей. Откаты тут просто серебряная пуля. Ты просто перечисляешь список возможных несуразностей в грамматике, а парсер будет планомерно подбирать подходящий вариант.
2. Преобразование результата парсирга в очень вольный AST с последующим анализом его с помощью сопоставления с образцом.

Задача бьется на две подзадачи:
1. Распознавание кода с учетом возможных повреждений.
2. Анализ поврежденного кода с применением шаблонов выявляющих характерные ошибки.

М>ну вот чем такое парсить-то?


Однозначно PEG-ом!

М>что из готовых инструментов вы посоветуете? у меня токенизация делается по списку ключевых слов. в первом проходе это будет read, close, return.


PEG позволяет избавиться от стадии лексического разбора, что позволит тебе выявлять и битые последовательности. Скажем не закрытую строку или сросшиеся литералы.

М> во втором проходе мы накладываем шаблон, специфичный для каждого ключевого слова и выделяем val, f, data.


Стадии у тебя должны быть такими:
1. Парсинг в алгебраические типы данных. Причем формат АлгТД должен быть таким чтобы учитывать возможность хранения битых последовательностей. Их можно по ходу дела наращивать.
2. Рекурсивный анализ полученной иерархии с использованием паттерн-матчинга (ПМ). ПМ позволит выявлять сразу паттерны, а не возиться с набором унылых if-ов.

М>в третьем проходе мы с учетом знания о поведении каждого "токена" строим "смысловые" связи и зависимости по данным. val -> ???, f <-- open(???), read() -> ?resilt?, return data, ?result?.


Вот это все можно записать в один шаблон. И тогда выявление подобных последовательностей станет банальным занятием. Ну, а уж дальше ты сможешь применить свои эвристики чтобы догадаться, что же пропущено.

VD>>Парсить регулярками — это из серии вырезать гланды через жопу автогеном.

М>это верно. но... пока остановился на таком варианте, т.к. он позволяет менять логику работы парсера на лету.

А зачем тут что-то делать на лету? Пойми, твоя задача не решается перебором. Тут нужно применять интеллект. Так что очень советую сменить стратегию.

VD>>В общем, мой тебе совет. Возьми подходящий инструмент и сделай свой прототип. Подходящий

VD>>интсрумент тут — это генератор парсеров. Я бы посоветовал бы взять наш Nemerle.Peg. Он грамматики любой сложности возьмет.
М>у нас половина людей под никсами, вторая половина под маками. код им понимать необязательно, но он у них должен по крайней мере работать. я не в курсе как у вас с кросс-платформенностью. под линухом заведется?

Во-первых, немерл, а стало быть и Nemerle.Peg прекрасно живут под Моно, а значит под Линуксом и Маком.
Во-вторых, ты же говоришь о прототипе. А его по фигу на чем писать. Ты должен только отработать идею и выявить то, что пока что скрыто от твоего взора. Так вот сделать это с PEG-ом, АлгТД (ака в немерле называются вариантами) и паттерн-матчингом будет в сто раз проще (я даже не преувеличиваю). Ну, а потом можешь это дело хоть на С переписать. У тебя уже будет понимание того как проблему нужно решать. Следовательно сложность уже будет другого порядка. Если переписывание окажется слишком сложным (что не мудрено, так как в языках где не доступен МП кода будет раз в 5 больше), то можно просто сгенерировать С-ишных код по типизированному представлению который тебе выдаст компилятор немерла.

М>и дело тут не в сложности грамматики. это JavaScript, VBS. под JS есть готовые парсеры/грамматики/whatever, но нету таких, которые бы отвечали условиям задачи (как в примере выше, который _очень_ типичный пример).


Я это прекрасно понимаю. Могу тебе привести в качестве примера похожей задачи написанные мной XML-литералы. Это собственно встроенный в язык XML, но со "вставками". Причем вставками кода. При этом грамматика ХМЛ-я не катит, так как вставки ее сильно расширяют. Соответственно невозможно использовать готовые парсеры ХМЛ-я. Но написать грамматику ХМЛ-я не составило труда, так как PEG очень мощный формализм.

Кстати, PEG очень похож на регулярные выражения. Так что если есть опыт работы с регулярками, но нет опыта работы с герераторами парсеров, то это еще один повод воспользоваться пегом.

Кстати, грамматика JavaScript (не знаю уж насколько полноценная) уже есть. Можно взять ее за основу и доработать напильником, так чтобы парсер брал и битый код.

>> Ну, а "битые входные данные" это ни что иное как более сложные грамматики. И PEG тут будет самым подходящим инструментом.

>> В прочем, любой генератор парсеров будет лучше чем регулярки или ручное написание парсера.
М>подскажите, пожалуйста, а то я уже мозг сломал -- как какому-нибудь стандартному генератору парсеров объяснить, что строка может не закрываться кавычкой или начинаться не с кавычки (т.е. ello, world\n'; var a = 'AAA'; ).

Создать правило которое будет выявлять эту незакрытость и парсить столько сколько получается.
С любым генератором парсеров тут будут проблемы, так как они в основном все лексерные, что требует выявления битости на уровне символов. Но вот как раз таки PEG безлексорынй. И еще в PEG-е есть офигительно мощная штука — предикаты. С их помощью можно тупо парсить строку следующим образом:
improperStringLiteral = "'"  (!(jsExpr / "'") any)*

Этот код означает, что мы просим парсер распознать последовательность начинающуюся с "'" (символ ' взятый в кавычки, если не понятно) и состоящую из любых сиволов, при условии, что символ не начинает последовательность удовлетворяющую выражению ЖабаСкрипта или символа "'". Оператор "!" означает негативный предикат. Правило идущее за ним не возвращает результата разбора. Оно только сопоставляется со входной строкой и в случае успеха правило any не будет разбираться, а разбор правила (!(jsExpr / "'") any)* будет остановлен. Таким образом негативный предикат позволяет задать синтаксическое правило останова разбора. Есть еще позитивный предикат "&" он остановит разбор в случае если идущее за ним правило провалится, и позволит двигаться дальше в обратном случае.

Собственно важно здесь именно возможность то что для выявления останова можно применять предикаты. Они позволят создать очень гибкие эвристики.

То что ты делаешь в науке называется восстановлением после обнаружения ошибки. Твоя задача восстановиться с минимальным пропуском значимых данных. Стратегия восстановления целиком зависит от тебя. А PEG предоставляет тебе очень удобный формализм в котором эта стратегия может быть выражена декларативно.

М>парсить "по науке" у меня не получается. ну не знаю я как описать такую грамматику,


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

М> а потому сначала выделяю ключевые слова ("ключевые" в смысле словарные), а уже отталкиваясь от них бью текст на токены, но это разбивка предположительная, и там приходится искать лучшее соответствие. я стрю семантическое дерево (если я правильно его называю),


Не правильно. Это называется синтаксическим деревом, или сокращенно AST (Abstract Syntax Tree).

М> в котором токены связаны во всех комбинациях, которые допускаются заранее заданными правилами и ищется самый длинный маршрут в дереве.


Тебе надо построить дерево которое получается, а потом начать выявлять паттетны ошибок. И для этого нужен другой механизм который есть во многих функциональных языках (в том числе в немреле, который я предлагаю использовать) — сопоставление с образцом. ПМ очень существенно упростит задачу распознования паттернов ошибок в коде.

В общем, это как раз тот случай когда сложность можно уменьшить применением подходящего инструмента и систематизацией своих знаний, а не грубой силой.

ЗЫ

Если решишься на применение немрела для прототипизрования, обещаю помочь по скайпу советами и другими консультациями.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: А как вы пишете сложный код?
От: samius Япония http://sams-tricks.blogspot.com
Дата: 11.03.11 12:01
Оценка: :)
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, мыщъх, Вы писали:


VD>Задача весьма странная. Интересно откуда у нее растут ноги? Что ломает эти скрипты?

Скрипты вбивали через аську, часто путая буквы и окна
Re[5]: А как вы пишете сложный код?
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.03.11 12:04
Оценка:
Здравствуйте, Sinix, Вы писали:

VD>>Ткнул на ссылки и увидел вполне вменяемые ответы. Может не хотел увидеть?

S>Нет, там ответы из серии "если такие задачи сложно решать с ФП-подходом — значит, мейнстрим отстой и надо от него отказаться".

Я этого не заметил. На мой взгляд (возможно поверхностный) в данном случае на тебя довлеет твой императивный опыт.

S>Напомню, исходный топик лежал не в философии и был посвящён конкретной проблеме на конкретной платформе. Соответственно, я ожидал практически применимых советов, не нарушающих FDG.


FDG — это что?

S>Если последовать совету ув. WolfHound, перейти к immutable спискам и хранить копии списков для undo-redo, как быть с биндингом, с вычислением изменений, с уведомлением об изменениях (они нужны как минимум для ограничений)?


Да никаких проблем с этим нет. Неизменяемые данные хороши тем, что позволяют переиспользовать неизмененные структуры данных, что практически избавляет от дублирования данных. В общем, WolfHound дело говорит.

S>Нам придётся имитировать поведение BindingList|ObservableCollection, и весь выигрыш от первоначальной идеи уйдёт на допиливание обёрток. Я не просто так, абстрактно говорю — я делал вполне рабочий прототип и делал замеры. В таком виде идея не взлетит.


Я не знаю твой задачи, по этому ничего утверждать не возьмусь. Скажу только что на счет неизменяемых структур ты не прав. Они как раз обеспечивают дешевую версинность данных, облегчают создание многопоточного кода и никак не мешают разным оповещениям и т.п. В прочем, зачастую при применении версионных данных оповещения становятся вообще не нужны. Но это отдельный разговор.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: А как вы пишете сложный код?
От: Sinix  
Дата: 11.03.11 12:26
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Я этого не заметил. На мой взгляд (возможно поверхностный) в данном случае на тебя довлеет твой императивный опыт.

VD>Да никаких проблем с этим нет. Неизменяемые данные хороши тем, что позволяют переиспользовать неизмененные структуры данных, что практически избавляет от дублирования данных. В общем, WolfHound дело говорит.

Не, это никогда не закончится Как можно 3 поста подряд игнорировать (и ведь каждый раз одно и то же пишу)

Если последовать совету ув. WolfHound, перейти к immutable спискам и хранить копии списков для undo-redo, как быть с биндингом, с вычислением изменений, с уведомлением об изменениях (они нужны как минимум для ограничений)?

Нам придётся имитировать поведение BindingList|ObservableCollection, и весь выигрыш от первоначальной идеи уйдёт на допиливание обёрток. Я не просто так, абстрактно говорю — я делал вполне рабочий прототип и делал замеры. В таком виде идея не взлетит.

и продолжать гнуть линию о тотальном превосходстве immutable в сфероконических условиях?

VD>FDG — это что?

Framework Design Guidelines, вестимо. Обязательно для чтения при разработке публичного API.

VD>Я не знаю твой задачи, по этому ничего утверждать не возьмусь.

Если совсем-совсем упрощённо — general-purpose список с возможностью согласованного undo-redo (undo-redo нескольких списков одновременно). Увы, это только часть задачи.
Re[3]: А как вы пишете сложный код?
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.03.11 12:37
Оценка: +1
Здравствуйте, Sinix, Вы писали:

VD>>На мой взгляд есть всего один способ писать сложный код — продумать как и что делать, а потом сесть и написать.

S>Ок, как быть, когда _детально_ придумать что и как делать на порядки сложнее/дороже по времени, чем просто написать код? Когда известна архитектура, известно, как (примерно) будет выглядеть API, но сам код можно написать несколькими способами?

Надо сесть и еще раз хорошенько подумать.
В процессе раздумий можно написать несколько тестов...

S>Я ведь уже приводил кучу примеров, когда попытки решить задачу с наскока сваливались в перебор всего дерева вариантов. Дошли до аргументации в духе "а нафига мейнстрим, если в нём не работает ФП-подход?".


Как я понял аргументы были не про мэйнстрим, а про неподходящие подходы для решения конкретных задач, которые приняты в мэйснтриме.

S>и лёгкие наезды аля

S>

S>Очень советую почитать статью IT о видах сложностей. Ты явно не читал ее, или читал по диагонали.


S>Предлагаю закругляться.


Хорошая идея. А статью все же прочитай/перечитай. Это не наезд, а очень мудрая мысль. Я ее перечитывал три раза. Вот думаю еще раз перечитать. При поверхностном изложении в ней заложены глубокие мысли.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: А как вы пишете сложный код?
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.03.11 12:38
Оценка:
Здравствуйте, samius, Вы писали:

VD>>Задача весьма странная. Интересно откуда у нее растут ноги? Что ломает эти скрипты?

S>Скрипты вбивали через аську, часто путая буквы и окна

Хм. То что вбивается через аську предназначено для чтения человеком. Это же идеи а не рабочий код. Не уж-то вы такой код пытаетесь в продакшен помещать?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: А как вы пишете сложный код?
От: samius Япония http://sams-tricks.blogspot.com
Дата: 11.03.11 12:40
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, samius, Вы писали:


VD>>>Задача весьма странная. Интересно откуда у нее растут ноги? Что ломает эти скрипты?

S>>Скрипты вбивали через аську, часто путая буквы и окна

VD>Хм. То что вбивается через аську предназначено для чтения человеком. Это же идеи а не рабочий код. Не уж-то вы такой код пытаетесь в продакшен помещать?

То была шутка не в тему. Я вообще к этому отношения не имею.
Re[8]: А как вы пишете сложный код?
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.03.11 12:46
Оценка:
Здравствуйте, samius, Вы писали:

VD>>Хм. То что вбивается через аську предназначено для чтения человеком. Это же идеи а не рабочий код. Не уж-то вы такой код пытаетесь в продакшен помещать?

S>То была шутка не в тему. Я вообще к этому отношения не имею.

Да, я когда уже послал ответ, то понял, что автор был другой. Но вопрос действительно интересный. Я смысл в подобном вижу только если это некая IDE и ей нужно разбирать ошибочный код.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: А как вы пишете сложный код?
От: Sinix  
Дата: 11.03.11 12:54
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Надо сесть и еще раз хорошенько подумать.

VD>В процессе раздумий можно написать несколько тестов...
Ок, тогда сойдёмся на том, что исходный пост — как раз про этап написания тестов и ?

S>>Я ведь уже приводил кучу примеров, когда попытки решить задачу с наскока сваливались в перебор всего дерева вариантов. Дошли до аргументации в духе "а нафига мейнстрим, если в нём не работает ФП-подход?".

VD>Как я понял аргументы были не про мэйнстрим, а про неподходящие подходы для решения конкретных задач, которые приняты в мэйснтриме.
Начали этим:

Если код получается сложным значит ты идешь не тем путем или используешь плохой инструмент.
Тебе говорили как свести решение к тривиальному но ты не послушал.

а закончили — в ответ на описание проблем, которые бывают на практике, а не в абстрактных спорах — вот этим:

Технологии менстрима заставляют писать неоправданно сложный код.

Получается, мы культурно оплевали друг друга и вернулись к исходному вопросу: как быть, когда приходится писать тот самый "неоправданно-сложный код"?

VD>Хорошая идея. А статью все же прочитай/перечитай. ... При поверхностном изложении в ней заложены глубокие мысли.

Полностью поддерживаю (читал и саму статью, и последующий холивар)
Re[4]: А как вы пишете сложный код?
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.03.11 12:59
Оценка:
Здравствуйте, мыщъх, Вы писали:

Много написал, а ссылку забыл дать. Вот здесь
Автор(ы): Чистяков Владислав Юрьевич
Дата: 07.06.2011
Макрос PegGrammar – это макрос Nemerle, позволяющий добавлять в приложения парсеры, описываемые в нотации PEG.
описан макрос PegGrammar. Там и кратенькое описание PEG-а, есть и ссылки на примеры парсеров.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: А как вы пишете сложный код?
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.03.11 13:08
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Начали этим:

S>

S>Если код получается сложным значит ты идешь не тем путем или используешь плохой инструмент.

Я готов был бы подписаться од этим, если бы в понятие "сложность" не вкладывалось бы столько разных смыслов.

S>а закончили — в ответ на описание проблем, которые бывают на практике, а не в абстрактных спорах — вот этим:

S>

S>Технологии менстрима заставляют писать неоправданно сложный код.


Ты предложил свое, не верное на взгляд Вольфхаунда, решение которые было навеяно тебе твоими привычками и знаниями. Что он еще мог на это сказать?
Пойми, ты видишь решение задач только в рамках имеющихся у тебя заготовок решений. Когда тебе предложили решение к которому ты не привык, то ты сразу же начал с ним бороться вместо того чтобы попробовать осмыслить его. При таких условиях что тебе не скажи, все равно будет не понимание.

S>Получается, мы культурно оплевали друг друга и вернулись к исходному вопросу: как быть, когда приходится писать тот самый "неоправданно-сложный код"?


Выходит, что ты отбрасываешь не подходящие тебе ответы и возвращаешься к неверной постановке задачи.

VD>>Хорошая идея. А статью все же прочитай/перечитай. ... При поверхностном изложении в ней заложены глубокие мысли.

S>Полностью поддерживаю (читал и саму статью, и последующий холивар)

Тогда обрати внимание на мысль о том, что сложность количественная может быть переведена в сложность интеллектуальную. Именно по этому задачи неберучки для новичков использующих "стандартные" (читай — мэйнстрим) решаются опытными товарищами с применением подходящих инструментов и подходов.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: А как вы пишете сложный код?
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 11.03.11 13:08
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Закончим маленьким примером. Прототип:

...
S>Хоть результат и не блещет внутренней красотой — это не public api, и от него не требуется универсальность и переиспользуемость — но, по крайней мере, он отражает _намерения_ программиста куда лучше, чем предыдущий код.

А что этот код хоть делать должен ?

S>Во-вторых, основная цель поста — показать, как на практике пишется "сделай не-знаю-что"-код. Ключевое слово — на практике, т.е. всё вышеперечисленное работает как минимум у меня.


S>3. Используй xDD (подставить нужное).

S>С радостью. Делитесь — как решаете такие задачи вы?

Кент Бек написал целую книгу про это Первые главы в ДДД тоже сгодятся
Re: А как вы пишете сложный код?
От: minorlogic Украина  
Дата: 11.03.11 20:45
Оценка:
Из сумбурного описания , сложилось впечатление, что основная сложность была в " а что собсственно я должен реализовать?"
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Ищу работу, 3D, SLAM, computer graphics/vision.
Re[3]: А как вы пишете сложный код?
От: vdimas Россия  
Дата: 11.03.11 21:44
Оценка: -1
Здравствуйте, VladD2, Вы писали:

VD>Ну, а "битые входные данные" это ни что иное как более сложные грамматики. И PEG тут будет самым подходящим инструментом. В прочем, любой генератор парсеров будет лучше чем регулярки или ручное написание парсера.


Дык сами грамматики отлаживать тоже надо. Любой генератор парсеров хорош лишь для заведомо отлаженной грамматики.
Re[5]: А как вы пишете сложный код?
От: мыщъх США http://nezumi-lab.org
Дата: 16.03.11 20:54
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, мыщъх, Вы писали:


М>>...

cjhhb за зажержку. обмозговывал ваш пост. дотнет за разумное время не осилю. сейчас и так по приказу партии курю пиона, рубина и sql. думаю -- а что если это как-то аусорсить тому, что уже это знает? вам -- реклама языка (все-таки мы уже в интел, а это контора большая) плюс деньги (разумные) за выполнения заказа.

VD>Задача весьма странная. Интересно откуда у нее растут ноги? Что ломает эти скрипты?

есть пассивные сенсоры, работающие на сетевом уровне, ассемблирующие TCP и передающие данные на мой уровень. вот только на моем уровне обнаруживается, что сборка реально кривая и целые фрагменты физически отсутствуют. одна из причин -- потеря пакетов на сенсоре. по статистике 10%. остальные причины -- хз. но в итоге у меня ~15% скриптов выходят "битыми" и мне нужно что-то с этим делать, т.к. разработчики сенсора фикс обещают только в светлом коммунистическом будущем, а кушать хочется здесь и сейчас.

так что задача далеко не теоретическая. пример реально битого скрипта привожу ниже.

var heapSprayToAddress = 0x05050505;
var shellcode = unescape("%u9090%u9090 ... %u6a51%uff00raySlide;
    }
    spraySlide = spraySlide.substring(0,spraySlideSize/2);
    return spraySlide;


как видно, тут мы потеряли кавычку, закрывающую круглую скобку и какую-то конструкцию, открывающую фигурную скобку, в которую мы "врезаемся".

данный скрипит писали хакеры-идиоты, и потому он автоматически реконструировался еще в ранних релизах. переменная raySlide очевидно _sp_raySlide, что распознается по словрю. но даже если бы она была не spraySlide, а foo, то по конструкции 0x5050505/unescape/substring легко распознать что это такое.

к сожалению, даже легкого "запутывания" кода достаточно для ослепления тривиального поиска по шаблонов (в частности, substring можно реализовать и "на месте" через for плюс доступ к строке по индексу.) и потому приходится прикручивать спец-движок js, который будет готов к тому, что переменные/методы/функции окажутся неопределенными или неправильными.

в принципе, аналогичная техника уже обкатана в антивирусных эмуляторах x86, они тоже готовы к тому, что работать приходится с кусками кода, разбавленного чем-то непотребным. однако, для js скриптов готовых решений нет (во всяком случае на паблике).

VD>>>Руками? А почитать что-нить по теме парсеров не пробовал для начала? Не все задачи берутся методом замучивания.

М>>читаю книгу дракона (второе издание, ценую в сто баксов).
VD>Это бесполезное занятие. Особенно если это русский вариант (перевод). Книга сильно устарела и не содержит передовых подходов.
где же я возьму в штатах русский перевод? обижаете. из русского я с собой только пару книг пелевина взял.

М>>а что вы еще можете порекомендовать?

VD>Давай лучше на ты.
давай. только я могу забыть и снова перейти на "вы" по привычке



М>>сейчас я все это могу распарсить на регулярках, т.е. понять, что 'olla-la-shit-happens' это одно, а f.read это уже другое. именно f.read, а не happensf.read, т.к. дальше идет f.close.


VD>В твое задаче просто напрашивается:

VD>1. Применение PEG-а для распознавания битых последовательностей. Откаты тут просто серебряная пуля.
VD>Ты просто перечисляешь список возможных несуразностей в грамматике, а парсер будет планомерно подбирать подходящий вариант.
а как перечислить несуразности? тут даже удаления комментариев и разбивку на строки делать не получается, т.к. вот тут у нас есть открывающая кавычка, а вот парная ей закрывающая. и все вроде бы парсится без ошибок. но... это начало от одной строки и конец от другой. а между ними добрая половина скрипта.

то же самое с комментариями. даже если мы их парсим без ошибок, это ничего не значит. нужно еще посмотреть что у нас в комментариях и если там подозрительно много осмысленного кода, то тут одно из двух -- либо он закомментирован, либо скрипт битый. либо и то, и другое.

поэтому, описать _грамматику_ не получается. можно задать только набор словарных слов и отношений между ними...

М>>что из готовых инструментов вы посоветуете? у меня токенизация делается по списку ключевых слов. в первом проходе это будет read, close, return.


VD>PEG позволяет избавиться от стадии лексического разбора, что позволит тебе выявлять и битые последовательности.

VD>Скажем не закрытую строку или сросшиеся литералы.
пока у меня такой алгоритм. после того как мы нашли unescape поиском по подстроке (без парсинга), на начинаем анализировать аргументы:

"var shellcode = unescape("%u9090%u9090 ... %u6a51%uff00raySlide;
}
"
в данном случае мы видим, что есть неожиданный перенос строки, следовательно уже косяк. затем мы выделяем из строки (от открывающихся кавычек до переноса) подстроку "raySlide", которая есть фрагмент переменной. от начала переменной и дальше идет осмысленный код, который парсится без ошибок. таким образом, за не именем лучших предположений мы относим raySlide; к границе строки, устанавливая внутренний флаг, что строка повреждена и потому все данные, опирающиеся на нее, достоверны только с некоторым приближением.

то есть у меня идет смесь от поиска по известным "словарным" словам (именам библиотечных функций, операторам), поиск по "словарным" словам, генерируемых налету (в данном случае это имя пременной). два соседних словарных слова валидируются "классическим" парсингом. то есть получаются постоянные скачки между регулярными выражениями и парсингом.

скорее у меня ближе к нормальному парсингу с той лишь оговоркой, что первичная токенизация делается путем словарного поиска, а так же поиска по шаблонам вида x = foo ( a1, a2, a3 ) [; | EOL | .bar ].

полученные токены служат опорными точками. много их извлекается из комментов и строк (на js куча кода в строках), и уже потом решается что делать с пространством между ними.

VD>Стадии у тебя должны быть такими:

VD>1. Парсинг в алгебраические типы данных. Причем формат АлгТД должен быть таким чтобы учитывать возможность хранения битых последовательностей. Их можно по ходу дела наращивать.
пока с типами данных у меня трудности. достаточно очевидно, что "родных" типов js для решения задачи не хватает. в частности, a = foo()/b = bar(a). тут 'a' может быть (физически) и Array, и String, но у меня указывается, что 'a' это тот тип, который возвращается foo и который хочет bar (при условии, что они связаны примерно как open и read).

М>>у нас половина людей под никсами, вторая половина под маками. код им понимать необязательно, но он у них должен по крайней мере работать. я не в курсе как у вас с кросс-платформенностью. под линухом заведется?

VD>Во-первых, немерл, а стало быть и Nemerle.Peg прекрасно живут под Моно, а значит под Линуксом и Маком.
про моно я слышал, но был не в курсе работает ли под ним Nemerle.Peg, ну раз вы говорите, что работает... (от коллег слышал, что под моно вообще мало что дотнетовского работает, но сам никогда не юзал ни дотнет, ни моно)

VD>Во-вторых, ты же говоришь о прототипе. А его по фигу на чем писать.

на чем писать -- по фиг, но если он будет запускаться только на моей машине... вот тут коллега написал скрипт на руби, который работает только на никсах, хотя юзает rails и потому должен (в теории) запускаться и на винде. но у меня запустить его не получилось. и я написал свой. на питоне. у меня одна часть лучше, у коллеги другая часть лучше. у меня выдает больше данных, у коллеги -- данные более "чистые". и мы щас мержим каждый сам свою версию, портируя с питона на рубин и с рубина на питон...

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

VD>Кстати, грамматика JavaScript (не знаю уж насколько полноценная) уже есть. Можно взять ее за основу и доработать напильником, так чтобы парсер брал и битый код.

я бы с удовольствием это аутсорсил, попросив у руководства денежек для финансирования этой затеи. есть желающие? и сколько это примерно может стоить? т.к. это не кодинг, а главным образом ресерч, то догадываюсь, что и стоить будет соответственно, но хоть примерно?

М>> а потому сначала выделяю ключевые слова ("ключевые" в смысле словарные), а уже отталкиваясь от них бью текст на токены, но это разбивка предположительная, и там приходится искать лучшее соответствие. я стрю семантическое дерево (если я правильно его называю),

VD>Не правильно. Это называется синтаксическим деревом, или сокращенно AST (Abstract Syntax Tree).
неее... что такое AST я знаю. но у меня все-таки семантическое дерево, т.е. дерево описывающее _смысл_ функций, в частности, указывается, что такое open и чем оно отличается от close.

тут в соседней ветке про абби пишут о дереве понятий в естественных языках. так вот, хоть это и смешно, но у меня что-то похожее получается для языка программирования. даже если названия функций ничего не говорят, из самой последовательности вызовов и схемы передачи аргументов и возвращенных значений можно много чего "вытянуть". ну например: a = new b(c); a.foo = xxx; a.bar = yyy; a.baz = ooo; ooo = a; тут как бы сразу понятно из какой это оперы. с точки зрения семантики это интерцепт (не важно хакерский или легальный), с точки зрения AST...

VD>Тебе надо построить дерево которое получается, а потом начать выявлять паттетны ошибок. И для этого нужен другой механизм который есть во многих функциональных языках (в том числе в немреле, который я предлагаю использовать) — сопоставление с образцом. ПМ очень существенно упростит задачу распознования паттернов ошибок в коде.


вот! как раз у меня есть обрацов очень много, в том числе и образцов алгоритмов, которые ожидаются в коде, а алгоритм зачастую опознается по его небольшому куску. а совокупность распознанных алгоритмов -- это паттерн более высокого уровня, описывающий поведение всей программы в целом.

к сожалению, от паттерна более высокого уровня пришлось отказаться. хакеры стали брать готовые огромные скрипты (например, скрипт "шахматы") и добавлять к нему небольшой кусок зловредного кода. в этом случае, распознанный паттерн высокого уровня говорит: да это что-то гуевое такое, с виду не зловредное. увы...

VD>Если решишься на применение немрела для прототипизрования, обещаю помочь по скайпу советами и другими консультациями.

заранее благодарен. а вот если бы вы согласились еще и помочь с самим прототипированием, то моя благодарность не знала бы границ (в том числе и финансовых)
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Re[6]: А как вы пишете сложный код?
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.03.11 02:32
Оценка:
Здравствуйте, мыщъх, Вы писали:

М>cjhhb за зажержку. обмозговывал ваш пост. дотнет за разумное время не осилю. сейчас и так по приказу партии курю пиона, рубина и sql. думаю -- а что если это как-то аусорсить тому, что уже это знает? вам -- реклама языка (все-таки мы уже в интел, а это контора большая) плюс деньги (разумные) за выполнения заказа.


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

Потом еще вопрос денег интересен. Если деньги будут серьезными, то много кто возьмется.

М>есть пассивные сенсоры,


Это что?

М>работающие на сетевом уровне, ассемблирующие TCP и передающие данные на мой уровень. вот только на моем уровне обнаруживается, что сборка реально кривая и целые фрагменты физически отсутствуют. одна из причин -- потеря пакетов на сенсоре. по статистике 10%. остальные причины -- хз. но в итоге у меня ~15% скриптов выходят "битыми" и мне нужно что-то с этим делать, т.к. разработчики сенсора фикс обещают только в светлом коммунистическом будущем, а кушать хочется здесь и сейчас.


Круто. Это значит из-за сбоя в железки вы какой-то догадыватель пишите?
А как потом такие скрипты используются? Не уж то выполняются?

М>так что задача далеко не теоретическая. пример реально битого скрипта привожу ниже.


М>
М>var heapSprayToAddress = 0x05050505;
М>var shellcode = unescape("%u9090%u9090 ... %u6a51%uff00raySlide;
М>    }
М>    spraySlide = spraySlide.substring(0,spraySlideSize/2);
М>    return spraySlide;
М>


М>как видно, тут мы потеряли кавычку, закрывающую круглую скобку и какую-то конструкцию, открывающую фигурную скобку, в которую мы "врезаемся".


Если мы из него "угадаем":
var heapSprayToAddress = 0x05050505;
var shellcode = unescape("%u9090%u9090 ... %u6a51%uff00raySlide");
    spraySlide = spraySlide.substring(0,spraySlideSize/2);
    return spraySlide;

этого будет достаточно?

М>однако, для js скриптов готовых решений нет (во всяком случае на паблике).


Да я вообще впервые вижу подобную задачу.

VD>>Ты просто перечисляешь список возможных несуразностей в грамматике, а парсер будет планомерно подбирать подходящий вариант.

М>а как перечислить несуразности? тут даже удаления комментариев и разбивку на строки делать не получается, т.к. вот тут у нас есть открывающая кавычка, а вот парная ей закрывающая. и все вроде бы парсится без ошибок. но... это начало от одной строки и конец от другой. а между ними добрая половина скрипта.

Как, как? Сначала пробуем прогнать обычны правила. Если они все обломались, то переходим в режим восстановления после обнаружения ошибки. Этот режим будет пытаться применить эдакие эвристические правила которые будут пытаться выявить несуразности по шаблонам. Если и это не удастся, то начинаем с места где не удалось спарсить код выполнять пропуск символов до тех пор пока код снова не начнет парситься. Полученные пропущенный диапазон анализируем отдельно и с пристрастием.

М>то же самое с комментариями. даже если мы их парсим без ошибок, это ничего не значит. нужно еще посмотреть что у нас в комментариях и если там подозрительно много осмысленного кода, то тут одно из двух -- либо он закомментирован, либо скрипт битый. либо и то, и другое.


Ну, тут уж ничего не поделаешь. Должна быть какая-то логика. Ни то ты весь закоментированный код автоматом откометаришь .

М>поэтому, описать _грамматику_ не получается. можно задать только набор словарных слов и отношений между ними...


Что-то это больно походит на гадание на кофейно гуще.

М>"var shellcode = unescape("%u9090%u9090 ... %u6a51%uff00raySlide;

М> }
М>"
М>в данном случае мы видим, что есть неожиданный перенос строки, следовательно уже косяк.

А разве жабасктипт не допускает перенос строк внутри строк?

М>затем мы выделяем из строки (от открывающихся кавычек до переноса) подстроку "raySlide", которая есть фрагмент переменной.


На каком основании? Может быть программист хотел написать что=то вроде "raySlide=" + spraySlide

М> от начала переменной и дальше идет осмысленный код, который парсится без ошибок. таким образом, за не именем лучших предположений мы относим raySlide; к границе строки, устанавливая внутренний флаг, что строка повреждена и потому все данные, опирающиеся на нее, достоверны только с некоторым приближением.



VD>>Стадии у тебя должны быть такими:

VD>>1. Парсинг в алгебраические типы данных. Причем формат АлгТД должен быть таким чтобы учитывать возможность хранения битых последовательностей. Их можно по ходу дела наращивать.
М>пока с типами данных у меня трудности. достаточно очевидно, что "родных" типов js для решения задачи не хватает. в частности, a = foo()/b = bar(a). тут 'a' может быть (физически) и Array, и String, но у меня указывается, что 'a' это тот тип, который возвращается foo и который хочет bar (при условии, что они связаны примерно как open и read).

Я не о тех типах вел речь. Я говорил, что тебе надо сначала свалить битый код в АСТ поддерживающее битый код. А потом уже разбираться с теми его частями которые битые (используя при этом информацию из не битых).

Короче надо не за один проход все проблемы решать, а за два, а луче на три. На первом просто разбираешь синтаксис (выявляя битые части), а на втором делаешь анализ. На третьем собираешь из битого АСТ гипотетический целый.

М>про моно я слышал, но был не в курсе работает ли под ним Nemerle.Peg, ну раз вы говорите, что работает... (от коллег слышал, что под моно вообще мало что дотнетовского работает, но сам никогда не юзал ни дотнет, ни моно)


На самом деле не так мало. А пег — это всего лишь парсер. Он только доступ к строке по индексу и использует.

VD>>Во-вторых, ты же говоришь о прототипе. А его по фигу на чем писать.

М>на чем писать -- по фиг, но если он будет запускаться только на моей машине... вот тут коллега написал скрипт на руби, который работает только на никсах, хотя юзает rails и потому должен (в теории) запускаться и на винде. но у меня запустить его не получилось. и я написал свой. на питоне. у меня одна часть лучше, у коллеги другая часть лучше. у меня выдает больше данных, у коллеги -- данные более "чистые". и мы щас мержим каждый сам свою версию, портируя с питона на рубин и с рубина на питон...

Ну, или у вас прототип. Или вы все же пишите уже что-то рабочее. Прототип нужен только для пробы идей. Далее уже по прототипам вы напишите рабочую версию.

М>прототип предполагается показывать коллегами и спрашивать их мнение, а так же добиваться поддержки и понимания вышестоящего руководства, а для этого прототип должен запускаться на их машинах с пол-пинка. установка дополнительных библиотек или чего-либо не приветствуется, ибо сокращает кол-во желающих поиграться с этой штукой. вот потому и нужно, чтобы работало на всем зоопарке.


А не проще ли позвать коллег к себе, показать им прототип и рассказать суть концепций?

VD>>Кстати, грамматика JavaScript (не знаю уж насколько полноценная) уже есть. Можно взять ее за основу и доработать напильником, так чтобы парсер брал и битый код.

М>я бы с удовольствием это аутсорсил, попросив у руководства денежек для финансирования этой затеи. есть желающие?

Создай тему здесь. А вось кто-то и найдется. Поработать за деньги на немерле может оказаться многим интересно. Только задание какое-то неопределенное, что может отпугнуть.

М>и сколько это примерно может стоить?


Черт его знает. Ты задачу только в общих чертах описал. Все зависит от деталей.

М>>> а потому сначала выделяю ключевые слова ("ключевые" в смысле словарные), а уже отталкиваясь от них бью текст на токены, но это разбивка предположительная, и там приходится искать лучшее соответствие. я стрю семантическое дерево (если я правильно его называю),

VD>>Не правильно. Это называется синтаксическим деревом, или сокращенно AST (Abstract Syntax Tree).
М>неее... что такое AST я знаю. но у меня все-таки семантическое дерево, т.е. дерево описывающее _смысл_ функций, в частности, указывается, что такое open и чем оно отличается от close.

А это не верный подход. Не надо перепрыгивать через стадии. Сначала надо получить синтаксическое дерево, а уже потом по нему бежать и вычислять разные семантические значения. Это как раз в Дракоен описано хорошо.

М>тут в соседней ветке про абби пишут о дереве понятий в естественных языках. так вот, хоть это и смешно, но у меня что-то похожее получается для языка программирования.


В ЯП семантика однозначая, так что с этим никаких проблем нет. А вот для естественных языков понятность — это высший пилотаж, точнее ИИ.

М>даже если названия функций ничего не говорят, из самой последовательности вызовов и схемы передачи аргументов и возвращенных значений можно много чего "вытянуть". ну например: a = new b(c); a.foo = xxx; a.bar = yyy; a.baz = ooo; ooo = a; тут как бы сразу понятно из какой это оперы. с точки зрения семантики это интерцепт (не важно хакерский или легальный), с точки зрения AST...


Ну, вот когда код разобран, то такой паттерн распознать будет не сложно. Но не надо сваливать стадию парсинга и распознавания паттерна в одну стадию.

М>к сожалению, от паттерна более высокого уровня пришлось отказаться. хакеры стали брать готовые огромные скрипты (например, скрипт "шахматы") и добавлять к нему небольшой кусок зловредного кода. в этом случае, распознанный паттерн высокого уровня говорит: да это что-то гуевое такое, с виду не зловредное. увы...


Антивирусы пишите?

VD>>Если решишься на применение немрела для прототипизрования, обещаю помочь по скайпу советами и другими консультациями.

М>заранее благодарен. а вот если бы вы согласились еще и помочь с самим прототипированием, то моя благодарность не знала бы границ (в том числе и финансовых)

Ну, сам я не обещаю. У меня загрузка выше крыши. Но кто-то может и найдется. Начать надо с качественной постановки задачи. Потому как даже из этого краткого примера не все ясно. Вопросов слишком много остается. Ну, и конечно сумму стоит озвучить. Ведь если она будет приличной, то многие отложат свои дела в сторону.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: А как вы пишете сложный код?
От: Don Reba Канада https://stackoverflow.com/users/49329/don-reba
Дата: 17.03.11 02:43
Оценка: :)
Интересный скрипт. У меня тут антивирус старательно блокирует эту ветку дискуссии и на сайте и в почте.
Ce n'est que pour vous dire ce que je vous dis.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.