Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.237.15.145] |
|
Сообщ.
#1
,
|
|
|
Всем прива!
Некоторое время назад эта тема затрагивалась вскользь. ЕМНИП в связи с обсуждением оператора goto. Хотелось бы обсудить, и, скажем так, поставить все точки над ï ... Пусть есть код непростой обработки: for (size_t i1 = 0; i1< IMAX; ++i1) { for (size_t i2 = 0; i2< IMAX; ++i2) { for (size_t i3 = 0; i3< IMAX; ++i3) { for (size_t i4 = 0; i4< IMAX; ++i4) { for (size_t i5 = 0; i5< IMAX; ++i5) { // ─────► нам нужно остановиться тут, запомнить состояние, а потом вернуться сюда for (size_t i87 = 0; i87< IMAX; ++i87) { // ... } } } } } } Какие варианты, кроме как использовать метки и оператор goto, могут быть для решения данного вопроса? |
Сообщ.
#2
,
|
|
|
А если каждый цикл представить функцией?
Т.е. будет функция NextStep, которая будет в себе заключать счетчики первых 5 циклов. И функция SubFor, которая будет реализовывать цикл i87. Тогда для каждого шага циклов можно будет вызывать функцию NextStep, а после выполнения каких-либо действий запускать SubFor. |
Сообщ.
#3
,
|
|
|
Цитата macomics @ А если каждый цикл представить функцией? Мне кажется, как в той аксиоме "От перемены мест слагаемых - легче не становится". Нужно что-то другое, менее "линейное", как мне представляется. Другой подход к выстраиванию обработки. Но это только на уровне "идеи". Что и как - пока не знаю и не понимаю. Добавлено Цитата macomics @ Т.е. будет функция NextStep, Попробуй выразить кодом. Обсудим. |
Сообщ.
#4
,
|
|
|
см. пост выше.
|
Сообщ.
#5
,
|
|
|
Цитата macomics @ см. пост выше. Пока не вижу "сохранения" и "восстановления". Напиши, плс, какой-то синтетический пример этого. |
Сообщ.
#6
,
|
|
|
Вот же
void SubFor(size_t MAXI, ptrdiff_t ind[]) { for (ptrdiff_t i87 = 0; i87 < MAXI; ++i87) { // ... } } bool NextStep(size_t MAXI, ptrdiff_t i[]) { if (++i[5] >= MAXI) { // for (size_t i5 = 0; i5 < MAXI; ++i5) i[5] = 0; if (++i[4] >= MAXI) { // for (size_t i4 = 0; i4 < MAXI; ++i4) i[4] = 0; if (++i[3] >= MAXI) { // for (size_t i3 = 0; i3 < MAXI; ++i3) i[3] = 0; if (++i[2] >= MAXI) { // for (size_t i2 = 0; i2 < MAXI; ++i2) i[2] = 0; if (++i[1] >= MAXI) // for (size_t i1 = 0; i1 < MAXI; ++i1) return false; } } } } return true; } int main() { ptrdiff_t ind[6]; // счетчики for (ptrdiff_t i = 0; i < 6; ++i) ind[i] = 0; // инициализация первого шага do { // ─────► нам нужно остановиться тут, запомнить состояние, а потом вернуться сюда SubFor(10, ind); // внутренний цикл } while (NextStep(10, ind)); // следующий шаг 5 внешних циклов } |
Сообщ.
#7
,
|
|
|
Наверное я туплю ... В случае с использованием goto, для восстановления обработки в месте прерывания - мы читаем сохраненные i1,i2,i3,i4,i5 и прыгаем в точку обработки (восстанавливая эти i*). А у тебя как?
|
Сообщ.
#8
,
|
|
|
А у меня они изначально хранятся в массиве из N элементов. NextStep приращивает старший счетчик в массиве и если он превысил MAXI, тогда его обнуляет, увеличивает и проверяет следующий счетчик и т.д. до первого. Как только i1 превысит или станет равным MAXI функция возвращает false и цикл обработки заканчивается. В функцию же SubFor передается этот массив счетчиков для проведения обработки.
При этом все это может быть встроено в любой линейный алгоритм через равное/произвольное число операций, а в конце может стоять цикл do как у меня в примере. Можно обойтись только NextStep если SubFor должен всегда выполняться перед этой функцией. bool NextStep(size_t MAXI, ptrdiff_t i[]) { if (i[0] == 0) for (ptrdiff_t i87 = 0; i87 < MAXI; ++i87) { // ... } if (++i[5] >= MAXI) { // for (size_t i5 = 0; i5 < MAXI; ++i5) i[5] = 0; if (++i[4] >= MAXI) { // for (size_t i4 = 0; i4 < MAXI; ++i4) i[4] = 0; if (++i[3] >= MAXI) { // for (size_t i3 = 0; i3 < MAXI; ++i3) i[3] = 0; if (++i[2] >= MAXI) { // for (size_t i2 = 0; i2 < MAXI; ++i2) i[2] = 0; if (++i[1] >= MAXI) // for (size_t i1 = 0; i1 < MAXI; ++i1) i[0] = 1; } } } } return i[0] == 0; } int main() { ptrdiff_t ind[6]; // счетчики for (ptrdiff_t i = 0; i < 6; ++i) ind[i] = 0; // инициализация первого шага // ─────► нам нужно остановиться тут, запомнить состояние, а потом вернуться сюда NextStep(10, ind); // ─────► нам нужно остановиться тут, запомнить состояние, а потом вернуться сюда NextStep(10, ind); // ─────► нам нужно остановиться тут, запомнить состояние, а потом вернуться сюда NextStep(10, ind); // ─────► нам нужно остановиться тут, запомнить состояние, а потом вернуться сюда while (NextStep(10, ind)); // следующий шаг } |
Сообщ.
#9
,
|
|
|
Честно говоря - я пока не андестенд, сказывается принятый алкоголь.
Завтра попробуем на практических примерах. |
Сообщ.
#10
,
|
|
|
Цитата Majestio @ Какие варианты, кроме как использовать метки и оператор goto, могут быть для решения данного вопроса? Если такое потребовалось, то вероятно где-то проблемы.. Кроме того, вот прямо если так, то 5 циклов не нужно. Достаточно одного с границей в 5*IMAX. Если это просто задача, можно и так: const size_t IMAX=10; int state=0; size_t i1=0,i2=0,i3=0,i4=0,i5=0; for(int eoj=0;eoj==0;) { switch(state) { case 0: // это состояние исключительно с демонстрационными целями. i1=i2=i3=i4=i5=0; state=1; break; case 1: if(++i5 == IMAX) { i5=0; if(++i4 == IMAX) { i4=0; if(++i3 == IMAX) { i3=0; if(++i2 == IMAX) { i2=0; if(++i1 == IMAX) { i1=0; state=2; break; } } } } } { // что-то делаем for (size_t i87 = 0; i87< IMAX; ++i87) { // ещё что-то делаем } } break; case 2: // возможно, если нужно : // eoj=1; // закончили работу. Выход. break; } // делаем что-то совсем другое. Другой автомат состояний. } |
Сообщ.
#11
,
|
|
|
Цитата Majestio @ Мне кажется, как в той аксиоме "От перемены мест слагаемых - легче не становится". Нужно что-то другое, менее "линейное", как мне представляется. Запихни всё, что требуется делать внутри цикла, где "нам надо остановиться тут", в отдельную функцию. А все 5 внешнийх циклов преврати в один, сразу по пяти индексам, хоть так, как выше показано. И всё, сначала ты эти пять индексов проинициализируешь нулями, а после восстановления, собственно, восстановленными. |
Сообщ.
#12
,
|
|
|
Цитата ЫукпШ @ Если это просто задача, можно и так: Ну я ему изначально предложил именно это. Только вместо switch .. case использовал функцию, которая как раз содержит одну из веток этого case. |
Сообщ.
#13
,
|
|
|
Цитата ЫукпШ @ if(++i5 == IMAX) { i5=0; if(++i4 == IMAX) { i4=0; if(++i3 == IMAX) { i3=0; if(++i2 == IMAX) { i2=0; if(++i1 == IMAX) Ещё один, в циклы не умеющий Тут же while очевидный, который и понятнее, и пишется быстрее. Как принцип - сойдёт, в реальности же это эталон, достойный govnokod.ru |
Сообщ.
#14
,
|
|
|
А тут и не должно быть циклов. Эта функция должна сделать один инкремент для этих 5 циклов.
|
Сообщ.
#15
,
|
|
|
Цитата Majestio @ Пусть есть код непростой обработки: Можно еще нахеровертить 87 вложенных циклов и написать "Пусть есть код непростой обработки:... И как тут обойтись без goto"... Да очень просто - если есть вот такая шняга, значит ты ничего кроме циклов и goto не умеешь. Приведи реальный пример когда надо именно 5-6 вложенных циклов и без этого никуда? Это наверное из области теоретической физики или че это? Или для реализации сверхмощной нейронной сети? И то я слабо верю что там ну никак не разбить эти циклы. В описываемом тобой случае с вероятностью 99.9999% - неверный подход к решению задачи. |
Сообщ.
#16
,
|
|
|
Цитата OpenGL @ Тут же while очевидный, который и понятнее, и пишется быстрее. Как принцип - сойдёт, в реальности же это эталон, достойный govnokod.ru Здесь нет циклов, это инкремент счётчиков. Цикл один, внешний, для всех работающих автоматов. |
Сообщ.
#17
,
|
|
|
Цитата macomics @ А тут и не должно быть циклов. Эта функция должна сделать один инкремент для этих 5 циклов. Вот это "не должно быть циклов"? Цитата macomics @ if (++i[5] >= MAXI) { // for (size_t i5 = 0; i5 < MAXI; ++i5) i[5] = 0; if (++i[4] >= MAXI) { // for (size_t i4 = 0; i4 < MAXI; ++i4) i[4] = 0; if (++i[3] >= MAXI) { // for (size_t i3 = 0; i3 < MAXI; ++i3) i[3] = 0; if (++i[2] >= MAXI) { // for (size_t i2 = 0; i2 < MAXI; ++i2) i[2] = 0; if (++i[1] >= MAXI) // for (size_t i1 = 0; i1 < MAXI; ++i1) return false; } } } } return true; } Тут скорее вопрос может возникать - как в принципе тут очевидный цикл можно не увидеть? Добавлено Цитата ЫукпШ @ Здесь нет циклов, это инкремент счётчиков. Инкремент, который легко запихнуть в цикл. Вон сделай как Киля выше предложил - 87 счётчиков. Накопипастишь 87 одинаковых условий, которые буквой цикла отличаются, или в этом случае лень уже пересилит, и заставит в цикл это обернуть? |
Сообщ.
#18
,
|
|
|
Вот и написал бы свой вариант (с циклом), который однозначно лучше прежних.
|
Сообщ.
#19
,
|
|
|
А смысл? Если кто-то не видит, как из набора последовательности однотипных условий, отличающихся друг от друга одним индексом, сделать цикл, то человек просто далёк от программирования, но тогда он вряд ли бы тут вообще появился. А если видит, то пусть идёт в другое место, кормить я не буду его.
|
Сообщ.
#20
,
|
|
|
Цитата OpenGL @ Инкремент, который легко запихнуть в цикл. я делаю именно то, что мне нужно в данном алгоритме. Подобное использование счётчиков носит принципиальный характер для решения именно поставленной задачи. Поскольку цикл - один. Он не только для данного автомата. Но и для всех других, которые потребуются. |
Сообщ.
#21
,
|
|
|
Цитата OpenGL @ Если кто-то не видит, как из набора последовательности однотипных условий, отличающихся друг от друга одним индексом, сделать цикл, то человек просто далёк от программирования, но тогда он вряд ли бы тут вообще появился. А если видит, то пусть идёт в другое место, кормить я не буду его. А если кто-то не понимает почему приведен именно вариант без цикла, тогда вам тоже не стоит загромождать эту тему бесполезными сообщениями. Все прямо такие вумные как вутки, только вотруби не клюют! |
Сообщ.
#22
,
|
|
|
Цитата macomics @ А если кто-то не понимает почему приведен именно вариант без цикла, тогда вам тоже не стоит загромождать эту тему бесполезными сообщениями. Он все правильно пишет. ТС выдумал свои циклы в надежде оправдать goto. Давай зайдем с другой стороны: Покажите юзкейс, чтоб был предмет разговора. Пока это похоже на унылое говно. Я могу так же 87 вложенных циклов сделать и задать тот же вопрос. Я выше именно это и сделал, собственно. И что? На кой хер нужны 6 вложенных циклов и еще останавливаться где то там и потом продолжать? Я ровно так же могу написать макрос - который будет функцией с пятью вложенными циклами и повпендюливать его в 10 мест и спросить - объясните, как тут без макроса жить то... Вот так же и с циклами из примера ТС. Да никак - это высосаный из пальца пример, нигде не нужный, ну разве что для оправдания goto в теме ТС. |
Сообщ.
#23
,
|
|
|
Цитата Wound @ Подсчёт статистики по счастливым билетам. Приведи реальный пример когда надо именно 5-6 вложенных циклов и без этого никуда? |
Сообщ.
#24
,
|
|
|
Смею вас заверить, что "такое никогда не нужно" суть любимый аргумент... та много какой темы. Множественного наследования реализаций например. Или ленивых вычислений. Увы, но "такое" иногда становится очень даже актуальным, и тогда ты сидишь и хреначишь костыль.
Добавлено Вы тут покостыльте, я пока за рыбкой к пиву сбегаю. |
Сообщ.
#25
,
|
|
|
Цитата Wound @ ТС выдумал свои циклы в надежде оправдать goto Киля, не кипятись! Смею тебя уверить, что оператор goto очень надежный. Он действительно может тебе помочь в трудных ситуациях. Но в этой теме не о том речь. Соглашусь, суть темы - обсуждение сферического коня в вакууме. Тем не менее, тема не перестает быть интересной. А если еще и бывает ... Цитата Qraizer @ Увы, но "такое" иногда становится очень даже актуальным, и тогда ты сидишь и хреначишь костыль. Бывает, и тогда вообще ахтунг! Прочитавши ваши рассуждения, я пришел к определенным выводам: Вместо много-вложенных циклов используем другой подход (псевдокод): // тут условие входа - или инициализация, или загрузка сохраненных данных while(1+1) { // 1) проверка на выход - все просчитали, или нет // 2) меняем переменные, даже если их 87 // 3) делаем единичный пересчет или сохраняемся для приостановки } |
Сообщ.
#26
,
|
|
|
Цитата Wound @ оправдать goto Я вчера использовал оператор goto! Правда не помню где. |
Сообщ.
#27
,
|
|
|
Цитата Majestio @ Какие варианты, кроме как использовать метки и оператор goto, могут быть для решения данного вопроса? Ну, можно костыли накостылять. Основной метод: инкапсулировать все состояния в один объект, его и сохранять/восстанавливать. Как-то так, например: import java.util.*; import java.lang.*; class Rextester { public static void main(String args[]) { Loop<State4> loop = new Loop<>(new State4(0,0,0,0), new State4(5,5,5,5), new State4(0,0,0,0)); System.out.println("Run from the start:"); run(loop, false).ifPresent(state -> { System.out.println("\nSave state 1: " + state); loop.cur = state; }); System.out.println("Continue from state 1: " + loop.cur); run(loop, true).ifPresent(state -> { System.out.println("\nSave state 2: " + state); loop.cur = state; }); System.out.println("Continue from state 2: " + loop.cur); run(loop, true).ifPresent(state -> { System.out.println("\nSave state 3: " + state); loop.cur = state; }); } static Optional<State4> run(Loop<State4> loop, boolean cont) { // restore int i1 = loop.cur.i1; int i2 = loop.cur.i2; int i3 = loop.cur.i3; int i4 = loop.cur.i4; for (; i1 < loop.max.i1; i1++) { for (; i2 < loop.max.i2; i2++) { for (; i3 < loop.max.i3; i3++) { if (i2 == 2 && i3 == 3) { if (cont) { cont = false; } else { // save return Optional.of(new State4(i1, i2, i3, i4)); } } if (i2 == 3 && i3 == 1) { if (cont) { cont = false; } else { // save return Optional.of(new State4(i1, i2, i3, i4)); } } for (; i4 < loop.max.i4; i4++) { System.out.printf("%d,%d,%d,%d; ", i1, i2, i3, i4); } i4 = loop.min.i4; } i3 = loop.min.i3; } i2 = loop.min.i2; } return Optional.empty(); } } class Loop<S> { S min; S max; S cur; Loop(S min, S max, S cur) { this.min = min; this.max = max; this.cur = cur; } } class State4 { int i1, i2, i3, i4; State4(int i1, int i2, int i3, int i4) { this.i1 = i1; this.i2 = i2; this.i3 = i3; this.i4 = i4; } @Override public String toString() { return String.format("(%d,%d,%d,%d)", i1, i2, i3, i4); } } => Run from the start: 0,0,0,0; 0,0,0,1; 0,0,0,2; 0,0,0,3; 0,0,0,4; 0,0,1,0; 0,0,1,1; 0,0,1,2; 0,0,1,3; 0,0,1,4; 0,0,2,0; 0,0,2,1; 0,0,2,2; 0,0,2,3; 0,0,2,4; 0,0,3,0; 0,0,3,1; 0,0,3,2; 0,0,3,3; 0,0,3,4; 0,0,4,0; 0,0,4,1; 0,0,4,2; 0,0,4,3; 0,0,4,4; 0,1,0,0; 0,1,0,1; 0,1,0,2; 0,1,0,3; 0,1,0,4; 0,1,1,0; 0,1,1,1; 0,1,1,2; 0,1,1,3; 0,1,1,4; 0,1,2,0; 0,1,2,1; 0,1,2,2; 0,1,2,3; 0,1,2,4; 0,1,3,0; 0,1,3,1; 0,1,3,2; 0,1,3,3; 0,1,3,4; 0,1,4,0; 0,1,4,1; 0,1,4,2; 0,1,4,3; 0,1,4,4; 0,2,0,0; 0,2,0,1; 0,2,0,2; 0,2,0,3; 0,2,0,4; 0,2,1,0; 0,2,1,1; 0,2,1,2; 0,2,1,3; 0,2,1,4; 0,2,2,0; 0,2,2,1; 0,2,2,2; 0,2,2,3; 0,2,2,4; Save state 1: (0,2,3,0) Continue from state 1: (0,2,3,0) 0,2,3,0; 0,2,3,1; 0,2,3,2; 0,2,3,3; 0,2,3,4; 0,2,4,0; 0,2,4,1; 0,2,4,2; 0,2,4,3; 0,2,4,4; 0,3,0,0; 0,3,0,1; 0,3,0,2; 0,3,0,3; 0,3,0,4; Save state 2: (0,3,1,0) Continue from state 2: (0,3,1,0) 0,3,1,0; 0,3,1,1; 0,3,1,2; 0,3,1,3; 0,3,1,4; 0,3,2,0; 0,3,2,1; 0,3,2,2; 0,3,2,3; 0,3,2,4; 0,3,3,0; 0,3,3,1; 0,3,3,2; 0,3,3,3; 0,3,3,4; 0,3,4,0; 0,3,4,1; 0,3,4,2; 0,3,4,3; 0,3,4,4; 0,4,0,0; 0,4,0,1; 0,4,0,2; 0,4,0,3; 0,4,0,4; 0,4,1,0; 0,4,1,1; 0,4,1,2; 0,4,1,3; 0,4,1,4; 0,4,2,0; 0,4,2,1; 0,4,2,2; 0,4,2,3; 0,4,2,4; 0,4,3,0; 0,4,3,1; 0,4,3,2; 0,4,3,3; 0,4,3,4; 0,4,4,0; 0,4,4,1; 0,4,4,2; 0,4,4,3; 0,4,4,4; 1,0,0,0; 1,0,0,1; 1,0,0,2; 1,0,0,3; 1,0,0,4; 1,0,1,0; 1,0,1,1; 1,0,1,2; 1,0,1,3; 1,0,1,4; 1,0,2,0; 1,0,2,1; 1,0,2,2; 1,0,2,3; 1,0,2,4; 1,0,3,0; 1,0,3,1; 1,0,3,2; 1,0,3,3; 1,0,3,4; 1,0,4,0; 1,0,4,1; 1,0,4,2; 1,0,4,3; 1,0,4,4; 1,1,0,0; 1,1,0,1; 1,1,0,2; 1,1,0,3; 1,1,0,4; 1,1,1,0; 1,1,1,1; 1,1,1,2; 1,1,1,3; 1,1,1,4; 1,1,2,0; 1,1,2,1; 1,1,2,2; 1,1,2,3; 1,1,2,4; 1,1,3,0; 1,1,3,1; 1,1,3,2; 1,1,3,3; 1,1,3,4; 1,1,4,0; 1,1,4,1; 1,1,4,2; 1,1,4,3; 1,1,4,4; 1,2,0,0; 1,2,0,1; 1,2,0,2; 1,2,0,3; 1,2,0,4; 1,2,1,0; 1,2,1,1; 1,2,1,2; 1,2,1,3; 1,2,1,4; 1,2,2,0; 1,2,2,1; 1,2,2,2; 1,2,2,3; 1,2,2,4; Save state 3: (1,2,3,0) — https://rextester.com/LYV19256 Добавлено Можно ещё попробовать с Continuation-passing style поиграть. Правда, продолжения в файл не сохранишь, так что не всегда подойдёт. |
Сообщ.
#28
,
|
|
|
Собственно, пример с продолжениями:
Скрытый текст #lang racket (define ((start save-and-exit)) (for* ((i1 (in-range 0 5)) (i2 (in-range 0 5)) (i3 (in-range 0 5))) (when (and (= i2 2) (= i3 3)) ; save (save-and-exit)) (when (and (= i2 3) (= i3 1)) ; save (save-and-exit)) (for ((i4 (in-range 0 5))) (printf "~a,~a,~a,~a; " i1 i2 i3 i4)))) (define (main) (define exit #f) (define state #f) (define (save-and-exit) (let/cc k (printf "~nSaving state...~n") (set! state k) (exit))) (define (loop) (let/cc k (set! exit k) (state))) (set! state (start save-and-exit)) (printf "Run from start:~n") (loop) (printf "~nContinue from state 1:~n") (loop) (printf "~nContinue from state 2:~n") (loop) (printf "~nContinue from state 3:~n") (loop) (printf "~ndone")) => > (main) Run from start: 0,0,0,0; 0,0,0,1; 0,0,0,2; 0,0,0,3; 0,0,0,4; 0,0,1,0; 0,0,1,1; 0,0,1,2; 0,0,1,3; 0,0,1,4; 0,0,2,0; 0,0,2,1; 0,0,2,2; 0,0,2,3; 0,0,2,4; 0,0,3,0; 0,0,3,1; 0,0,3,2; 0,0,3,3; 0,0,3,4; 0,0,4,0; 0,0,4,1; 0,0,4,2; 0,0,4,3; 0,0,4,4; 0,1,0,0; 0,1,0,1; 0,1,0,2; 0,1,0,3; 0,1,0,4; 0,1,1,0; 0,1,1,1; 0,1,1,2; 0,1,1,3; 0,1,1,4; 0,1,2,0; 0,1,2,1; 0,1,2,2; 0,1,2,3; 0,1,2,4; 0,1,3,0; 0,1,3,1; 0,1,3,2; 0,1,3,3; 0,1,3,4; 0,1,4,0; 0,1,4,1; 0,1,4,2; 0,1,4,3; 0,1,4,4; 0,2,0,0; 0,2,0,1; 0,2,0,2; 0,2,0,3; 0,2,0,4; 0,2,1,0; 0,2,1,1; 0,2,1,2; 0,2,1,3; 0,2,1,4; 0,2,2,0; 0,2,2,1; 0,2,2,2; 0,2,2,3; 0,2,2,4; Saving state... Continue from state 1: 0,2,3,0; 0,2,3,1; 0,2,3,2; 0,2,3,3; 0,2,3,4; 0,2,4,0; 0,2,4,1; 0,2,4,2; 0,2,4,3; 0,2,4,4; 0,3,0,0; 0,3,0,1; 0,3,0,2; 0,3,0,3; 0,3,0,4; Saving state... Continue from state 2: 0,3,1,0; 0,3,1,1; 0,3,1,2; 0,3,1,3; 0,3,1,4; 0,3,2,0; 0,3,2,1; 0,3,2,2; 0,3,2,3; 0,3,2,4; 0,3,3,0; 0,3,3,1; 0,3,3,2; 0,3,3,3; 0,3,3,4; 0,3,4,0; 0,3,4,1; 0,3,4,2; 0,3,4,3; 0,3,4,4; 0,4,0,0; 0,4,0,1; 0,4,0,2; 0,4,0,3; 0,4,0,4; 0,4,1,0; 0,4,1,1; 0,4,1,2; 0,4,1,3; 0,4,1,4; 0,4,2,0; 0,4,2,1; 0,4,2,2; 0,4,2,3; 0,4,2,4; 0,4,3,0; 0,4,3,1; 0,4,3,2; 0,4,3,3; 0,4,3,4; 0,4,4,0; 0,4,4,1; 0,4,4,2; 0,4,4,3; 0,4,4,4; 1,0,0,0; 1,0,0,1; 1,0,0,2; 1,0,0,3; 1,0,0,4; 1,0,1,0; 1,0,1,1; 1,0,1,2; 1,0,1,3; 1,0,1,4; 1,0,2,0; 1,0,2,1; 1,0,2,2; 1,0,2,3; 1,0,2,4; 1,0,3,0; 1,0,3,1; 1,0,3,2; 1,0,3,3; 1,0,3,4; 1,0,4,0; 1,0,4,1; 1,0,4,2; 1,0,4,3; 1,0,4,4; 1,1,0,0; 1,1,0,1; 1,1,0,2; 1,1,0,3; 1,1,0,4; 1,1,1,0; 1,1,1,1; 1,1,1,2; 1,1,1,3; 1,1,1,4; 1,1,2,0; 1,1,2,1; 1,1,2,2; 1,1,2,3; 1,1,2,4; 1,1,3,0; 1,1,3,1; 1,1,3,2; 1,1,3,3; 1,1,3,4; 1,1,4,0; 1,1,4,1; 1,1,4,2; 1,1,4,3; 1,1,4,4; 1,2,0,0; 1,2,0,1; 1,2,0,2; 1,2,0,3; 1,2,0,4; 1,2,1,0; 1,2,1,1; 1,2,1,2; 1,2,1,3; 1,2,1,4; 1,2,2,0; 1,2,2,1; 1,2,2,2; 1,2,2,3; 1,2,2,4; Saving state... Continue from state 3: 1,2,3,0; 1,2,3,1; 1,2,3,2; 1,2,3,3; 1,2,3,4; 1,2,4,0; 1,2,4,1; 1,2,4,2; 1,2,4,3; 1,2,4,4; 1,3,0,0; 1,3,0,1; 1,3,0,2; 1,3,0,3; 1,3,0,4; Saving state... done |
Сообщ.
#29
,
|
|
|
Цитата Majestio @ Киля, не кипятись! Смею тебя уверить, что оператор goto очень надежный. я в детстве прочитал одну хорошую книгу, вот отрывок: Цитата Запомните, что я вам скажу: один стакан рому вас, конечно, не убьет, но если вы выпьете один стакан, вам захочется выпить еще и еще. И клянусь вам моим париком: если вы не бросите пить, вы в самом скором времени умрете. Понятно? – Так помните, я говорю вам по чистой совести: слово «ром» и слово «смерть» для вас теперь означают одно и то же. От одного специалиста мне достались для работы некие важные исходные тексты. Он использовал С компилятор как ассемблер и goto - его любимый оператор. Надо ли продолжать описание ситуации ? =:0 А вот так нельзя было ? for (size_t i1 = 0; i1< IMAX; ++i1) { for (size_t i2 = 0; i2< IMAX; ++i2) { for (size_t i3 = 0; i3< IMAX; ++i3) { if(какое то условие) iWorkFlag=1; else iWorkFlag=0; for (size_t i4 = 0; i4< IMAX; ++i4) { if(какое то условие) iWorkFlag=2; for (size_t i5 = 0; i5< IMAX; ++i5) { // ─────► нам нужно остановиться тут, запомнить состояние, а потом вернуться сюда // iWorkFlag = 0 - ничего не делаем // iWorkFlag = 1 - работаем, пока флаг установлен // iWorkFlag = 2 - работаем один раз, для этого флаг сбрасываем SomeRoutine(iWorkFlag); // фактически ушли куда-то, потом вернулись. Состояние не испортили. for (size_t i87 = 0; i87< IMAX; ++i87) { // ... } } } } } } |
Сообщ.
#30
,
|
|
|
Цитата ЫукпШ @ А вот так нельзя было ? И ведь самый очевидный ответ был самым невероятным. |
Сообщ.
#31
,
|
|
|
Цитата ЫукпШ @ А вот так нельзя было ? В изначальном примере от Qraizer'а состояние сохранялось в файл, а при повторном запуске программы — восстанавливалось. Предлагаешь в качестве «восстановления» прокручивать циклы в холостую с самого начала, пока он не дойдёт до нужных значений счётчиков? |
Сообщ.
#32
,
|
|
|
Цитата korvin @ В изначальном примере от Qraizer'а состояние сохранялось в файл, а при повторном запуске программы — восстанавливалось. Предлагаешь в качестве «восстановления» прокручивать циклы в холостую с самого начала, пока он не дойдёт до нужных значений счётчиков? Состояние переменных не обязательно спасать, если они не портятся. Переменные-счётчики циклов (да и все остальные, какие нужно) нигде не портятся независимо от их расположения. Например, в стеке. А если они не портятся то и восстановления не требуют. Циклы прокручивать не нужно, поскольку вызов процедуры происходит внутри них |
Сообщ.
#33
,
|
|
|
Цитата ЫукпШ @ Состояние переменных не обязательно спасать, если они не портятся. В смысле «не портятся»? Программа завершилась. Всё. Память перетёрлась другим процессом. Цитата ЫукпШ @ Переменные-счётчики циклов (да и все остальные, какие нужно) нигде не портятся независимо от их расположения. Например, в стеке. Ещё раз: программа завершила работу. На середине цикла. Штатно. Так задумано. Нужно сохранить счётчики в файл и при следующем запуске — восстановить. |
Сообщ.
#34
,
|
|
|
Цитата ЫукпШ @ ЫукпШ, можно по-разному. Только в понятности алгоритма, скорости его кодирования, скорости исполнения и простоте отладки goto даст фору на все четыре пункта вперёд. А вот так нельзя было ? ... Добавлено Поймите, любой костыль будет тратить больше ресурсов. Нарисуйте блок-схему любого алгоритма «делать что-то долго, поэтому предусмотреть возможность стать на паузу с сохранением состояния и продолжить позже» и внезапно увидите стрелку вовнутрь. Алгоритм исходно неструктурен. Любые попытки его структурировать сделают его хуже. Добавлено Цитата ЫукпШ @ Он дурак. Мало знать возможности своего инструмента, нужно их понимать. Любой аспект может быть в разных ситуациях как положительным, так и отрицательным фактором. Сам по себе goto не есть зло. Злом является его использование не к месту. Это справедливо для любого аспекта языка. Любого языка. Или ты думаешь, что классы в Плюсах только для ООП? А шаблоны только для параметризации типов? Я так думал 20 лет назад. Ещё я думал, что знаю C++. И думал я так не первый раз. И не последний, впрочем. А потом мне попалась книжка Александреску. От одного специалиста мне достались для работы некие важные исходные тексты. Он использовал С компилятор как ассемблер и goto - его любимый оператор. Надо ли продолжать описание ситуации ? =:0 |
Сообщ.
#35
,
|
|
|
Было же уже. Не помню только, в холиварах или в C/C++.
|
Сообщ.
#36
,
|
|
|
Цитата D_KEY @ Было же уже. Не помню только, в холиварах или в C/C++. Об этом и написано в первом сообщении: “Некоторое время назад эта тема затрагивалась вскользь.” Да, был пример от Qraizer’а |
Сообщ.
#37
,
|
|
|
Цитата korvin @ Об этом и написано в первом сообщении Ну так тут вроде ничего нового не появилось особо. |
Сообщ.
#38
,
|
|
|
Цитата D_KEY @ Ну так тут вроде ничего нового не появилось особо. Да нет - появилось! А именно ИМХО очень правильное высказывание Qraizer'а: Цитата Сам по себе goto не есть зло. Злом является его использование не к месту. Это справедливо для любого аспекта языка. Любого языка. Простой пример - программирование "автоматной логики". Это когда ваш алгоритм не расписывается "деревом", а расписывается "сетью". И приходится на части вычислений "впрыгивать" в какие-то ранее обозначенные конструкции. Определенные "профессионалы" сразу заявят - "говнокод - разноси по функциям". Они просто не помнят уже цену операторов помещения и извлечения из стека. Ну што сказать - поколение Пепси Вот тут и подойдет прекрасный оператор GOTO, который может гонять алгоритм по неизведанным путям. Спешал фор Киля! Я знаю твою лютую, можно сказать - бешенную ненависть к прекрасному оператору GOTO. Специально для тебя лучшие умы Сколково уже давно придумали "case-switch" подход. На языке программирования Цэ это будет примерно так: // ... int mode = init_value; while(1+1) Х switch (mode) { case 1: ( // if, run, modify mode, continue/break } case 2: ( // if, run, modify mode, continue/break } // ... case 'N': ( // if, run, modify mode, continue/break } default: break; } } //... ADD: сорян - изначально забыл все это хозяйство завернуть в бесконечный цикл! Просто забыл... Хотя ... по-сути, это просто "сахаро-заменитель", замещяющий использование прекрасного императора GOTO Что имеем по факту! |
Сообщ.
#39
,
|
|
|
Цитата Majestio @ Цитата D_KEY @ Ну так тут вроде ничего нового не появилось особо. Да нет - появилось! А именно ИМХО очень правильное высказывание Qraizer'а Могу ошибаться, но он и в прошлый раз что-то такое говорил |
Сообщ.
#40
,
|
|
|
Возможно. Повторение - мать учения, могу ошибаться
|
Сообщ.
#41
,
|
|
|
Цитата Majestio @ Вот тут и подойдет прекрасный оператор GOTO Отвратительный оператор в 99.9999% случаев. Но иногда может и стоит его использовать. Но нужно крепко подумать. А потом еще раз. Даже в этом кейсе с сохранением, как мне кажется, вполне и без него ок. |
Сообщ.
#42
,
|
|
|
Цитата D_KEY @ Отвратительный оператор Для программистов-дауншифтеров - самое то!!! |
Сообщ.
#43
,
|
|
|
Цитата D_KEY @ Конечно можно. Но вот насчёт ок... Приведу свой же структурный код ещё раз, несколько упростив детали:Даже в этом кейсе с сохранением, как мне кажется, вполне и без него ок. int i, j, k, l; int i0 = 0, j0 =i0, k0 =j0, l0 =k0; if (needRestore()) { bool ok = restoring(i0, j0, k0, l0); if (ok) std::cout << "The saved state is successfully restored." << std::endl; else return std::cerr << "The saved state restoring error. State file is wrong." << std::endl, 1; } for (i=i0; i<10;) { for (j=j0; j<10;) { for (k=k0; k<10;) { for (l=l0; l<10; ++l) { if (needSave()) { bool ok = saving(i0, j0, k0, l0); if (!ok) return std::cerr << "The state saving error." << std::endl, 1; return std::cout << "The state successfully saved." << std::endl, 2; } /* ... */ std::cout << "i = " << i << ", j = " << j << ", k = " << k << ", l = " << l << std::endl; Sleep(1); } l0 = ++k; } l0 = k0 = ++j; } l0 = k0 = j0 = ++i; } Добавлено Любой алгоритм может быть представлен в структурной форме. Это доказанная (?) теорема. Другое дело, а всегда ли надо. Я утверждаю, что нет, хотя и в крайне редких случаях. Добавлено P.S. Для меня лично эти вопросы тривиальны. Код с goto был проаппрувлен через три секунды после появления идеи о нём в голове. И реализован за минуту. И сразу работал, без отладки. И понятен (??) любому, кто его увидел. В сухом остатке мне как-то коллинеарно на эмоции, которые этот код у кого может вызвать. Кому не нравится если, волен писать стек лямбд, абстрактный объект, машину состояний и вообще всё что его душе угодно. Совсем уж чтоб: исключения диссонанса не вызывают? Куда уж ещё более неструктурно-то... |
Сообщ.
#44
,
|
|
|
Цитата Qraizer @ В сухом остатке мне как-то коллинеарно на эмоции, которые этот код у кого может вызвать. Кому не нравится если, волен писать стек лямбд, абстрактный объект, машину состояний и вообще всё что его душе угодно Ну будешь в одной команде с кем-то, будете холиварить. И думаю, что каждый человек, который будет видеть этот код с goto, будет задавать вопросы. Так что тут все зависит от того, с кем ты работаешь и какие правила есть в команде/организации |
Сообщ.
#45
,
|
|
|
Цитата D_KEY @ Ну будешь в одной команде с кем-то, будете холиварить. И думаю, что каждый человек, который будет видеть этот код с goto, будет задавать вопросы. Так что тут все зависит от того, с кем ты работаешь и какие правила есть в команде/организации Как обычно, ответ не по существу и ни о чём. Пять баллов! ) |
Сообщ.
#46
,
|
|
|
Цитата korvin @ Как обычно, ответ не по существу и ни о чём. По-моему очень хороший и конкретный ответ на конкретную реплику про отношение к мнению других. Наплевать на мнение других людей ты можешь тогда, когда один работаешь. А так с goto у тебя точно будут приключения в команде. Если тебе это подходит, то ок Ну или может быть команда скажет, что ок (я бы, наверное, заапрувил PR с таким использованием goto, хотя тоже зависит от), значит тоже все в порядке. Но я думаю, что код будет вызывать споры на ревью, а потом еще вопросы у каждого нового человека, который будет этот код смотреть. |
Сообщ.
#47
,
|
|
|
Цитата D_KEY @ К слову сказать. В каждой кампании есть определённые стандарты. На то, на сё. Не секрет. У нас они тоже есть. Я всегда (!) джунам говорю так: «Стандарты нужны, т.к. без них будет анархия. В анархии разбираться всегда сложно и дорого. Вы пришли, вам теперь курить, что понасоздано до вас вашими коллегами. После вас кто-то придёт и будет курить созданное вами. Анархия — зло. Так что будьте добры соблюдать стандарты. Чего бы там ни было. Это самое простое, что можно сделать, чтобы легко и быстро влиться в процесс и начинать приносить кампании пользу. Но. Любая крайность тоже зло. Жить строго по уставу нельзя. Любой закон имеет сферу применимости. Даже УК ограничен, к военным, например, он имеет опосредованное отношение. Я не хочу, чтобы вы знали стандарты назубок, я хочу, чтобы вы понимали, почему они такие. Когда я спрошу, откуда взялся вот этот вот конкретный пункт и почему он сформулирован не как-нибудь иначе, и вы сможете внятно ответить, вы мидлы. А если в вашей деятельности встретится ситуация, когда следование букве стандарта вредит его духу, то вы не должны бояться нарушить первое ради сохранения второго. Да, вам будут задавать вопросы типа "почему так", "с чего это ты вдруг решил..." и даже "не ахренел ли ты". Если вы готовы и на них отвечать, значит вы сеньоры. Но до тех пор, пока вы не сеньоры и даже не мидлы, стандарты ваше всё.»/ И думаю, что каждый человек, который будет видеть этот код с goto, будет задавать вопросы |
Сообщ.
#48
,
|
|
|
Вот что нашел=)
Цитата D_KEY @ У них разные initValue По поводу оптимизации - надо мерить. Добавлено Пока получается что-то вроде того: int i = 0, j = 0, k = 0; if (restore(&i, &j, &k)) goto resume; for(i = 0; i < 10; ++i) { for (j = i; j < 10; ++j) { for(k = j; k < 10; ++k) { resume: // ... } } } vs int i = 0, j = 0, k = 0; restore(&i, &j, &k); for(; i < 10; ++i, j = i, k = j) { for (; j < 10; ++j, k = j) { for(; k < 10; ++k) { // ... } } } vs int init_i = 0, init_j = 0, init_k = 0; bool is_init = restore(&init_i, &init_j, &init_k); for(int i = is_init ? init_i : 0; i < 10; ++i) { for (int j = is_init ? init_j : i; j < 10; ++j) { for(int k = is_init ? init_k : j; k < 10; ++k) { // ... } } } Мне кажется, что вариант с goto понятнее... Хотя второй тоже неплох, на мой взгляд. goto vs break & continue (сообщение #3594314) Там в окрестностях тоже интересно почитать |
Сообщ.
#49
,
|
|
|
Цитата Majestio @ Спешал фор Киля! Я знаю твою лютую, можно сказать - бешенную ненависть к прекрасному оператору GOTO. Специально для тебя лучшие умы Сколково уже давно придумали "case-switch" подход. На языке программирования Цэ это будет примерно так: Я goto юзал только в образовательных целях, case-switch - юзал может быть пару раз, и то скорее всего мне было лень думать, либо там был несущественный участок кода, где он как то вписывался. switch-case - по факту не несет какого то негативного оттенка, не имею против него ничего. Но - лично мне не нравится он из за того что очень сильно захламляет код - по сути метод/функция - содержащая switch - превращается в одну большую портянку. И чем больше case в switch - тем длинее портянка, ну и плюс ко всему - он очень сильно ограничен в своих возможностях, прям сильно, если бы не было таких ограничений(он требует константых выражений, в отличии от if например), то возможно бы я чаще его применял. По этому по возможности стараюсь избегать его, не то что он плохой, а просто потому что у меня к нему предвзятое отношение, неудобно потом мотать его, ну и у него очень ограниченный спектр применений. Ну а по теме, я пока остаюсь при своем мнении. Ни одного аргумента оправданного применения goto пока не привели. Все примеры синтетические, высосаные из пальца, типа: Попробуй без goto запрыгнуть в 10-вложенный цикл for, а че не можешь? Ну все, значит goto рулит. Лично мое мнение - если так стоит вопрос, значит у тебя уже проблемы, и ты пытаешься их костылями исправить. |
Сообщ.
#50
,
|
|
|
Цитата Majestio @ Какие варианты, кроме как использовать метки и оператор goto, могут быть для решения данного вопроса? С ходу вижу 2 решения: 1) засунуть всю эту процедуру в отдельный поток и дальше синхронизироваться сообщениями. 2) сделать инициализацию параметров. таким образом, на каждой итерации нужный идекс будет увеличиваться, а при старте функции все индексы тут восстановлены из массива. |
Сообщ.
#51
,
|
|
|
Ну што, пришло время предложить челендж!
Конечно все эти много-вложенные циклы - баловство. В реальности же чаще бывает гораздо проще. Но бывает и ппц. Это касается алгоритмов, где количество обрабатываемых параметров 100500, да и если они еще отчасти взаимозависимы. Нижеприведенный пример конечно чистая синтетика, и очень упрощенная. Но, уверяю, и не такое бывает в сложных системах. Подобный фарш я программил лет 15 назад. Проект назывался "Робот обновлений Консультант Плюс". Невыносимое количество условий и параметров для их выполнения. Поехали Вот блок-схема алгоритма работы (восстановление работы, сиречь, "впрыгивание" в точки сохранения - на блок-схеме нет): Условия Условия челенджа Прошу - не спойлерить! Написали программу, присоединили шифрованным архивом, архивы паролями вскроем через недельку, как все заинтересованные отпишутся. Желательно писать программу так, чтобы можно было бы ее прогнать в онлайн-компиляторе. Вводные данные Процесс-1 Безусловный инкремент нулевой ячейки массива. Процесс-2 Ячейку1 увеличивает на содержимое Ячейки3 + 1 Процесс-3 Если сумма Ячейки0 и Ячейки1 НЕ делиться нацело на три, то Ячейку2 увеличиваем на 7 Процесс-4 Если сумма Ячейки0 и Ячейки2 деленная по модулю 5 не равна 4 - увеличиваем Ячейку3 на еденицу Проверка-1 Возвращает true, если сумма всех ячеек кратна 2, иначе false Проверка-2 Возвращает true, если сумма всех ячеек деленная по модулю 7 больше нуля, иначе false Проверка-3 Возвращает true, если числа всех ячеек четные, иначе false Проверка-4 Возвращает true, если сумма всех ячеек больше 24, иначе false "Эталонные вычисления" Step: 0 Proc: 1 0 0 0 0 Step: 1 Proc: 3 1 0 0 0 Step: 2 Proc: 1 1 0 7 0 Step: 3 Proc: 3 2 0 7 0 Step: 4 Proc: 2 2 0 14 0 Step: 5 Proc: 3 2 1 14 0 Step: 6 Proc: 1 2 1 14 0 Step: 7 Proc: 4 3 1 14 0 Step: 8 Proc: 2 3 1 14 1 Step: 9 Proc: 4 3 3 14 1 Step: 10 Proc: 2 3 3 14 2 Step: 11 Proc: 3 3 6 14 2 Step: 12 Proc: 1 3 6 14 2 Step: 13 Proc: 4 4 6 14 2 Step: 14 Proc: 5 4 6 14 3 ---------------------------------------------------------------- Step: 9 Proc: 4 3 3 14 1 Step: 10 Proc: 2 3 3 14 2 Step: 11 Proc: 3 3 6 14 2 Step: 12 Proc: 1 3 6 14 2 Step: 13 Proc: 4 4 6 14 2 Step: 14 Proc: 5 4 6 14 3 ---------------------------------------------------------------- Смотрим. На нулевом шаге имеем в ячейках нули и начали обрабатывать Процессом-1. На первом шаге, после Процесса-1, видим, что нулевая ячейка инкрементировалась. А обрабатывает этот шаг Процесс-3 На втором шаге после Процесса-3, видим, что вторая ячейка получила значение 7. А обрабатывает этот шаг Процесс-1 .. ну и так далее, до разделительной линии На последнем шаге-14 (всего 15 шагов) ячейки получили значения 4,6,14, 3 - ну а Proc-5 - это просто "окончательный выход из обработки" Вторая часть того же алгоритма. Но заход в него будет не со значений Процесс-1, ячейки (0,0,0,0), а с якобы "сохраненного" состояния - Процесс-4, ячейки (3,3,14,1) Видим часть "довычислений", видим результаты идентичные. Я и номер шага сохранял, но это лишнее. Свою программу на С++ прикрепляю запароленным архивом. Вэлком - покажите себя на деле! Ценим компактность и понятность кода, естественно идентичность тестовых расчетов. Прикреплённый файлmajestio_algo.cpp.7z (794 байт, скачиваний: 39) |
Сообщ.
#52
,
|
|
|
Сообщ.
#53
,
|
|
|
Majestio, для решения подобных задач существует стандартный метод. И его все хорошо знают. Не удивлюсь, если бо́льшая часть решений будет основана на нём. Другое дело, что для синтетики с простыми Процессами она очень накладна.
|
Сообщ.
#54
,
|
|
|
Цитата korvin @ Скажи, а ты специально так всё расположил, чтобы сделать лапшу из стрелок? Не специально, честно. Кстати, а ты в чем рисовал? Добавлено Цитата Qraizer @ Majestio, для решения подобных задач существует стандартный метод. И его все хорошо знают. Не удивлюсь, если бо́льшая часть решений будет основана на нём. Другое дело, что для синтетики с простыми Процессами она очень накладна. Ну пусть будем думать, что процессов не 4, а 50. Ну так, на перспективу |
Сообщ.
#55
,
|
|
|
Цитата Majestio @ Кстати, а ты в чем рисовал? OmniGraffle |
Сообщ.
#56
,
|
|
|
Цитата korvin @ OmniGraffle Ниче так получилось, и шрифты красивые. А я в какой-то онлайн-хрени нарисовал по-быстрому. |
Сообщ.
#57
,
|
|
|
Цитата Majestio @ шрифты красивые Шрифты Iosevka — качай да пользуйся. |
Сообщ.
#58
,
|
|
|
Цитата Majestio @ Та без разницы. Накладные тоже возрастут пропорционально. Ну пусть будем думать, что процессов не 4, а 50. Ну так, на перспективу |
Сообщ.
#59
,
|
|
|
Совершенно непонятно, как должны работать «точки останова» и как симулировать останов в программе.
Прикреплённый файлmajestio_state_machine.ml.zip (1,24 Кбайт, скачиваний: 49) |
Сообщ.
#60
,
|
|
|
Цитата korvin @ Совершенно непонятно, как должны работать «точки останова» и как симулировать останов в программе. Ну в реальности можно все это выносить в отдельный поток, а на точках останова чекать атомарную переменную на предмет необходимости останова и сброса состояния на диск. Как самый элементарный вариант. Цитата korvin @ Iosevka Благодарю! Нашел, скачал, щя смогу тоже наводить красоту Вот еще похожий ничегошный так - Pragmata. Добавлено Цитата Qraizer @ Та без разницы. Накладные тоже возрастут пропорционально. Ну ок. Будет оказия - прими участие вот тут |
Сообщ.
#61
,
|
|
|
...
Прикреплённый файлmajestio_machine.rkt.zip (1,11 Кбайт, скачиваний: 38) |
Сообщ.
#62
,
|
|
|
...
Прикреплённый файлmajestio_machine.go.zip (1,11 Кбайт, скачиваний: 42) |
Сообщ.
#63
,
|
|
|
korvin,
Ну а если по-серйозке - могёшь запилить сие на SWI-Prolog? Уж больно любопытно! |
Сообщ.
#64
,
|
|
|
На другом форуме, где мы тоже были с korvin'ом, были крутые специалисты по прологу. Эх...
|
Сообщ.
#65
,
|
|
|
Цитата Majestio @ На другом форуме Сикундочку! Дай-ка линк на тот форум, плис! |
Сообщ.
#66
,
|
|
|
Цитата Majestio @ Некогда Вовсю пилю тулзы под октябрьский дидлайн. Пришлось даже пару фич зарубить до следующего. Будет оказия - прими участие вот тут |
Сообщ.
#67
,
|
|
|
Цитата Qraizer @ Некогда Хреново. Ну че поделаешь, потом, когда уже все "вскроется" и когда будешь свободен - запили че-нить жутко лаконичное на плюсатых |
Сообщ.
#68
,
|
|
|
Цитата Majestio @ Цитата Majestio @ На другом форуме Сикундочку! Дай-ка линк на тот форум, плис! Это было давно Его уже нет. progz.ru кажись назывался. |
Сообщ.
#69
,
|
|
|
Цитата D_KEY @ Его уже нет. progz.ru кажись назывался. ЕМНИП, он переименовался в hardforum.ru , но там мёртво. |
Сообщ.
#70
,
|
|
|
Оффтоп А у меня одного FF в линуксе (последний минт, сам FF тоже обновлён) не открывает 4 страницу этой темы? Опытным путём выяснил, что виновато, видимо, сообщение #51 этой темы. Что в нём такого и кто его написал? Интересно, откроется ли оно в истории сообщений? |
Сообщ.
#71
,
|
|
|
Цитата OpenGL @ Скрытый текст Опытным путём выяснил, что виновато, видимо, сообщение #51 этой темы. Что в нём такого и кто его написал? Скрытый текст Там вложение-архив с паролем. |
Сообщ.
#72
,
|
|
|
Скрытый текст Цитата OpenGL @ Интересно, откроется ли оно в истории сообщений? Под линухом неплохо работает браузер Chromium, попробуй поставить его параллельно с огнелисом. |
Сообщ.
#73
,
|
|
|
Скрытый текст Цитата OpenGL @ Интересно, откроется ли оно в истории сообщений? Цитата Majestio @ Под линухом неплохо работает браузер Chromium, попробуй поставить его параллельно с огнелисом. О, я нашёл виновника, кажется! Твоё сообщение ломает огнелис, и в истории сообщений у тебя тоже не отображается ничего |
Сообщ.
#74
,
|
|
|
Скрытый текст Цитата OpenGL @ О, я нашёл виновника, кажется! Твоё сообщение ломает огнелис, и в истории сообщений у тебя тоже не отображается ничего Я только челендж решил замутить |
Сообщ.
#75
,
|
|
|
На держи:
Прикреплённый файлChallenge.zip (90,65 Кбайт, скачиваний: 45) Добавлено Если что можно взять любой онлайн компилятор C# скопипастить код с блокнота и запустить. |
Сообщ.
#76
,
|
|
|
Скрытый текст Цитата Majestio @ Vivaldi тоже очень неплох. Вроде как бывшие разработчики старой-классической Opera его пилят. И все страницы этой темы в нем открываютсяПод линухом неплохо работает браузер Chromium, попробуй поставить его параллельно с огнелисом. |
Сообщ.
#77
,
|
|
|
v2
Прикреплённый файлChallenge_v2.zip (92,61 Кбайт, скачиваний: 46) |
Сообщ.
#78
,
|
|
|
Цитата OpenGL @ Оффтоп А у меня одного FF в линуксе (последний минт, сам FF тоже обновлён) не открывает 4 страницу этой темы? Опытным путём выяснил, что виновато, видимо, сообщение #51 этой темы. Что в нём такого и кто его написал? Интересно, откроется ли оно в истории сообщений? Скрытый текст У меня в убунте в FF все открывается. FF 103.0.1 (64-bit) |
Сообщ.
#79
,
|
|
|
Скрытый текст Цитата OpenGL @ О, я нашёл виновника, кажется! Не-не-не, не того нашел! funtoo-vm /home/majestio # uname -a Linux funtoo-vm 5.16.18_p1-r1-debian-sources #1 SMP PREEMPT Mon Aug 15 01:28:48 +03 2022 x86_64 GNU/Linux funtoo-vm /home/majestio # firefox --version Mozilla Firefox 103.0.1 Спецом поставил FF на свой тестовый Linux Funtoo. Полет нормальный. Такшта ... Скрытый текст |
Сообщ.
#80
,
|
|
|
OpenGL, возможно, дело не в самом FF, а в библиотеке 7z, или каком-нибудь плагине для FF+7z, раз у других открывается, и у тебя открываются страницы с другими архивами (zip).
|
Сообщ.
#81
,
|
|
|
Перво-наперво можно, после неудачной загрузки этой "проблемной" страницы, открыть в FF "Инструменты разработчика" (Ctrl+Shift+I или F12) и посмотреть вкладку "Консоль". Там возможно может быть полезная инфа.
Если нет - попробовать отладку (я этим не пользовался). |
Сообщ.
#82
,
|
|
|
Ха. Всё ещё интереснее. Ровно то же самое у меня наблюдается и в опере под виндой на совершенно другом компе. И в Edge, если туда залогиниться. Видимо, это что-то с моим профилем связанное, и фигня происходит на сервере, а не в браузере.
|
Сообщ.
#83
,
|
|
|
OpenGL, стукни vot-у, мож в логах что увидит.
|
Сообщ.
#84
,
|
|
|
Немного упростил Ocaml-версию.
Прикреплённый файлmajestio_machine.ml.zip (1,11 Кбайт, скачиваний: 44) Добавлено Цитата Wound @ 92,61 Кбайт 92 килобайта? o_O' Ты там абстрактную фабрику абстрактных конечных автоматов реализовал что ли? ))) |
Сообщ.
#85
,
|
|
|
Цитата korvin @ 92 килобайта? o_O' Ты там абстрактную фабрику абстрактных конечных автоматов реализовал что ли? ))) Не, он в архив засунул весь проект, видать студии. |
Сообщ.
#86
,
|
|
|
Цитата Majestio @ он в архив засунул весь проект, видать студии. Вряд ли для всего проекта студии… Цитата Wound @ …можно взять любой онлайн компилятор C# скопипастить код с блокнота и запустить. |
Сообщ.
#87
,
|
|
|
Цитата korvin @ 92 килобайта? o_O' Ты там абстрактную фабрику абстрактных конечных автоматов реализовал что ли? ))) Ну типа того, пароль выслал в личку. Там же надо было делать с нацелом на 50+ процессов, плюс это синтетический пример. А если процессы сложнее ? Добавлено Писать для этой задачи спагети код, както я даже не представляю как. Я сходу в стрелках запутался, пока ты не привел вторую диаграмму, а в коде как это выразить в монолитной функции - лично мне лень думать. |
Сообщ.
#88
,
|
|
|
Цитата Wound @ а в коде как это выразить в монолитной функции - лично мне лень думать. Э-м… А чего тут думать? for (;;) { switch state { case 1: // do process-1, decision-1 break; ... default: return; } |
Сообщ.
#89
,
|
|
|
Цитата korvin @ Э-м… А чего тут думать? Да ну нах, там же портянка такая получится, что просто жопа. Причем чем сложнее будут потом требования - тем сложнее станет эта портянка. Потом в итоге это дойдет до условного потолка, когда поддерживать это будет боль и ад. Тем более что: Цитата Majestio @ Ну пусть будем думать, что процессов не 4, а 50. Ну так, на перспективу Это уже на порядок усложнит поддержку и сопровождение. Добавлено А если таких процессов скажем не 4 и не 50, а неизвестно сколько заранее - то тут с этим свичем пролетаешь. Добавлено Ну и собственно по этой самой причине и goto тут пролетает как фанера над Москвой. Жду когда уже там окончание ивента, очень хочеца посмотреть на решение с goto, при условии что ТС обозначил еще 50 процессов на перспективу, он же полюбому в свою архитектуру с goto закладывал такой вариант, раз про него написал. |
Сообщ.
#90
,
|
|
|
Сообщ.
#91
,
|
|
|
Common Lisp на goto:
Прикреплённый файлmajestio_machine.lisp.zip (853 байт, скачиваний: 38) Добавлено Цитата Wound @ Да ну нах, там же портянка такая получится, что просто жопа. Никакой портянки. Цитата Wound @ Причем чем сложнее будут потом требования - тем сложнее станет эта портянка. Вот когда требования изменятся, тогда и будешь тратить время на рефакторинг. YAGNI Цитата Wound @ Потом в итоге это дойдет до условного потолка, когда поддерживать это будет боль и ад. Рефакторить нужно вовремя. Цитата Wound @ Тем более что: Цитата Majestio @ 12 сентября, 21:06 Ну пусть будем думать, что процессов не 4, а 50. Ну так, на перспективу Если у тебя 50 процессов, никак не сгруппированных в подсистемы, то у тебя в любом случае будет жопа, хоть switch/case'ом пиши, хоть объектами, хоть наглядную схему черти: запутаешься в переходах от объекта к объекту, и общая картина у тебя не соберётся в голове. Цитата Wound @ А если таких процессов скажем не 4 и не 50, а неизвестно сколько заранее - то тут с этим свичем пролетаешь. А если метеорит упадёт на датацентр? Цитата Wound @ Ну и собственно по этой самой причине и goto тут пролетает как фанера над Москвой. Тут ничего не пролетает. Цитата Wound @ при условии что ТС обозначил еще 50 процессов на перспективу, он же полюбому в свою архитектуру с goto закладывал такой вариант, раз про него написал. ТСу надо пересмотреть архитектуру проекта в первую очередь, в таком случае. А goto тут не при чём. |
Сообщ.
#92
,
|
|
|
Цитата korvin @ Никакой портянки. Портянка и спагети-код, к бабке не ходи. Цитата korvin @ Вот когда требования изменятся, тогда и будешь тратить время на рефакторинг. YAGNI Не не не, то что я озвучил - никак не нарушает принцип YAGNI, просто сам подход и архитектура - закладывается с таким прицелом, чтоб это потом проще было бы расширять и сопровождать. А не переписывать с нуля каждый раз. Цитата korvin @ Рефакторить нужно вовремя А есть какие то четкие критерии? Ты вот сделал для 5 процессов свой switch, завтра тебе скажут - у нас тут еще 2 процесса добавилось, ты их добавишь, потом тебе придут скажут - еще один, потом еще 2, и начиная со скольки ты задумаешься о том, что твой switch тебя уже порядком начал напрягать и порабы это все отрефакторить? Цитата korvin @ Если у тебя 50 процессов, никак не сгруппированных в подсистемы, то у тебя в любом случае будет жопа, хоть switch/case'ом пиши, хоть объектами, хоть наглядную схему черти: запутаешься в переходах от объекта к объекту, и общая картина у тебя не соберётся в голове. У меня точно жопы не будет, т.к. каждый процесс - рассматривается как отдельная сущность. И соответственно нет смысла рассматривать его в контексте всей системы. Вот когда само условие задачи изменится так, что сам процесс перестанет иметь смысл, вот тогда возможно мне и придется чего то пересматривать. А до этого - мне не нужно держать общую картину в голове. Цитата korvin @ А если метеорит упадёт на датацентр? Так это не что то из ряда вон выходящее, а вполне себе суровая реальность. Цитата korvin @ Тут ничего не пролетает. Ну разве что если сидеть выносить себе мозги этим goto... Цитата korvin @ ТСу надо пересмотреть архитектуру проекта в первую очередь. А goto тут не при чём. Ну я пока не видел его решения, может быть ему не придется ничего пересматривать, откуда я знаю. Добавлено Ну и плюс расписать всю его схему на if/else или switch|case - много ума не надо(это даже не интересно), я сразу же отказался от этого варианта, т.к. это сложнее, чем тот подход, который например, я выбрал. Про другие подходы - я не говорю, т.к. не видел. |
Сообщ.
#93
,
|
|
|
Цитата Wound @ закладывается с таким прицелом, чтоб это потом проще было бы расширять и сопровождать. Это и есть нарушение принципа YAGNI. Это «потом расширять» может никогда не наступить и в итоге у тебя overengineering на ровном месте. Цитата Wound @ А есть какие то четкие критерии? Ты вот сделал для 5 процессов свой switch, завтра тебе скажут - у нас тут еще 2 процесса добавилось, ты их добавишь, потом тебе придут скажут - еще один, потом еще 2, и начиная со скольки ты задумаешься о том, что твой switch тебя уже порядком начал напрягать и порабы это все отрефакторить? Более-менее есть: Цитата Каждый программист знает, что возможности нашего мозга не безграничны. Есть ограничение на количество вещей, о которых мы можем думать. Это наш рабочий лимит памяти. Есть старый миф о том, что человек может держать в памяти одновременно 7±2 объектов. Это называется "Магическое число семь" и оно на самом деле не очень точное. Последние исследования говорят о числе 4±1, а то и меньше. В любом случае — количество идей, которые мы можем держать одновременно в голове, весьма ограниченно. Цитата Wound @ У меня точно жопы не будет, т.к. каждый процесс - рассматривается как отдельная сущность. Но 1) он не отдельный: после него идёт проверка по результатам которой выбирается следующий процесс — вот тебе и связь. Только ты раскидал её по разным файлам. Теперь, чтобы восстановить картину, тебе понадобится сгенерировать какую-нибудь диаграмму классов/зависимостей, а ля UML, вместо того, чтобы сразу написать наглядный код. 2) ничто не мешает мне вынести реализации процессов в отдельные процедуры, что я ± и сделал в своих примерах, сохранив при этом общую схему в одном месте, прям как на картинке. case 1: process1(); state = decision1() ? 4 : 3; break; Ну а мой пример на Go позволяет так же всё довольно легко расщирять, при желании/необходимости и/или сохранять схему в одном месте. Цитата Wound @ Так это не что то из ряда вон выходящее, а вполне себе суровая реальность. Падающие на датацентры метеориты — суровая реальность? Цитата Wound @ Ну разве что если сидеть выносить себе мозги этим goto... Там нечем выносить. Применение goto там ничем не отличается от switch/case. Цитата Wound @ Ну я пока не видел его решения, может быть ему не придется ничего пересматривать, откуда я знаю. Архитектура «диктует» решение, а не наоборот. Добавлено Цитата Wound @ Ну и плюс расписать всю его схему на if/else или switch|case - много ума не надо(это даже не интересно), я сразу же отказался от этого варианта, т.к. это сложнее Ты сам себе противоречишь: определись, «много ума не надо» или «сложнее». Цитата Wound @ это сложнее, чем тот подход, который например, я выбрал. Твоё решение — 92 килобайта, решение ТС — меньше килобайта. Расскажи ещё раз про сложность. |
Сообщ.
#94
,
|
|
|
Цитата korvin @ Это и есть нарушение принципа YAGNI. Это «потом расширять» может никогда не наступить и в итоге у тебя overengineering на ровном месте. Судя по описанию в вики, принцип YAGNI нарушается тогда, когда ты делаешь то, что тебе нахер не упало, но по твоим ощущениям в будущем может пригодится. Например в моем подходе таких функций/методов нет. Там все нужно. А то тебя щас послушаешь, так выйдет так что и принципы SOLID противоречат YAGNI, и вообще все надо писать в main, спагети-кодом, а потом когда заказчик прибежит с очередными требованиями - срочно надо все рефакторить и обязательно снова спагети-кодом, а как иначе? Зачем усложнять да? Цитата korvin @ Более-менее есть: Ну тогда switch тут вообще не катит, последние же иследования говорят что 4+- 1, а у тебя аж 5 процессов в исходной задаче, вместе с default - 6 Цитата korvin @ 1) он не отдельный: после него идёт проверка по результатам которой выбирается следующий процесс — вот тебе и связь. Только ты раскидал её по разным файлам. Теперь, чтобы восстановить картину, тебе понадобится сгенерировать какую-нибудь диаграмму классов/зависимостей, а ля UML, вместо того, чтобы сразу написать наглядный код. А зачем мне ее востанавливать, если я делал по уже созданной схеме? Это какой то реверс-инжиниринг? Тогда даже в этом случае - понять что происходит у меня будет проще, чем с goto тем же. Если ты внимательно посмотришь - там кода на строчек 15-20, все остальное это всякие проверки, исключения и тело класса. А че, пустой класс с конструктором уже около 10 строк занимает. Цитата korvin @ 2) ничто не мешает мне вынести реализации процессов в отдельные процедуры, что я ± и сделал в своих примерах, сохранив при этом общую схему в одном месте, прям как на картинке. Ну так после ивента ТС скажет, а давайте теперь еще 45 процессов добавим, чтоб 50 получилось. И вся твоя наглядность рухнет, потому что как ты там выше написал чел больше 4 вещей одновременно воспринимать не может. А у тебя их 50. Цитата korvin @ Падающие на датацентры метеориты — суровая реальность? Суровая реальность когда система постоянно расширяется, и дополняется. Выдвигают новые условия, новые фичи и т.д. Цитата korvin @ Там нечем выносить. Применение goto там ничем не отличается от switch/case. Искать метку глазами, в незнакомом коде - то еще удовольствие, если тебе такой подход нравится - дело твое, я ж ниче против не имею. Только не нужно это преподносить как общепринятую практику. Цитата korvin @ Архитектура «диктует» решение, а не наоборот. Вообще не понял о чем ты тут. Я написал что - я не видел что сделал ТС. И не могу прогнозировать придется ему там все переписывать с нуля, или просто добавить еще с десяток меток. Добавлено Цитата korvin @ Ты сам себе противоречишь: определись, «много ума не надо» или «сложнее». Много ума не надо, а сложнее в том, что все это превращается в спагети-код. Цитата korvin @ Твоё решение — 92 килобайта, решение ТС — меньше килобайта. Расскажи ещё раз про сложность. Я тебе в личку скинул пароль от своего архива. Если бы ты его скачал и посмотрел что там, может быть ты бы понял почему там 92 килобайта. Добавлено Цитата korvin @ Теперь, чтобы восстановить картину, тебе понадобится сгенерировать какую-нибудь диаграмму классов/зависимостей, а ля UML, вместо того, чтобы сразу написать наглядный код. Ну и плюс в MSVS есть диаграмма классов, которую можно сгенерировать из уже написаной проги. Я вот ради прикола сгенерировал. Прекрасно востановилась схема ТС. Могу скинуть скрин если нужно. |
Сообщ.
#95
,
|
|
|
Цитата Wound @ Ну тогда switch тут вообще не катит, последние же иследования говорят что 4+- 1, а у тебя аж 5 процессов в исходной задаче, вместе с default - 6 Где из пять? Их четыре, stop/default — не процесс. И не нужно понимать всё буквально. Добавлено Цитата Wound @ Много ума не надо, а сложнее в том, что все это превращается в спагети-код. Нет, не превращается. Беготня по классам-родителям-наследникам туда-сюда — вот настоящее спагетти =) Добавлено Цитата Wound @ Ну и плюс в MSVS есть диаграмма классов, которую можно сгенерировать из уже написаной проги. Нет, чтобы код писать понятный, лучше добавить костыль, да? |
Сообщ.
#96
,
|
|
|
Цитата korvin @ Где из пять? Их четыре, stop/default — не процесс. И не нужно понимать всё буквально. Я изначально сделал 4, мне Majestio написал, сказал пятый тоже надо. Я выложил второй архив, чтоб максимально соответствовало условию. Но не суть важно. Цитата korvin @ Нет, не превращается. Беготня по классам-родителям-наследникам туда-сюда — вот настоящее спагетти =) Да ну, какая беготня? Я если честно не понимаю твоих аргументов. Добавлено Цитата korvin @ Нет, чтобы код писать понятный, лучше добавить костыль, да? А код проще некуда, и это к слову не костыль, особенно когда у тебя не хеловорлд, а огромный проект. Чтоб не рисовать схемки в тетрадочке, две кнопки ткнул, и смотришь связи. |
Сообщ.
#97
,
|
|
|
Цитата Wound @ Искать метку глазами, в незнакомом коде - то еще удовольствие Что там искать? Там код десять строчек. А искать ту реализацию метода интерфейса, которая действительно была вызвана, — вот это настоящее приключение. Добавлено Цитата Wound @ Я тебе в личку скинул пароль от своего архива. Если бы ты его скачал и посмотрел что там, может быть ты бы понял почему там 92 килобайта. А я скачал и посмотрел: куча бойлерплейта, и всякие вспомогательные файлы студии. |
Сообщ.
#98
,
|
|
|
Цитата korvin @ Что там искать? Там код десять строчек. А искать ту реализацию метода интерфейса, которая действительно была вызвана, — вот это настоящее приключение. Да то там искать, как не вижу goto, а я довольно редко его встречаю, в основном в индусских примерах WinAPI на MSDN'e, так сидишь смотришь - где там этот индус метки понаставил, и каким хером я туда попаду, и почему я не попаду в другое место. И вроде кода 40 строк, а сидишь как олень метки эти ищешь. И про какую ту реализацию метода ты говоришь? В чем у тебя проблемы с поиском методов я не понимаю? |
Сообщ.
#99
,
|
|
|
Цитата Wound @ Да ну, какая беготня? Я если честно не понимаю твоих аргументов. Что, никогда стектрейсы фреймворков с DI-контейнерами не разгребал, например? ) |
Сообщ.
#100
,
|
|
|
Цитата korvin @ А я скачал и посмотрел: куча бойлерплейта, и всякие вспомогательные файлы студии. Ну так вот тебе и ответ откуда там 92 килобайта. Уж извини не я делал плюсы и всякие сишарпы, но с другой стороны и руками я его не писал, IDE все само генерило. Добавлено Цитата korvin @ Что, никогда стектрейсы фреймворков с DI-контейнерами не разгребал, например? ) А причем тут что я разгребал и то что мы обсуждаем? Я тебе могу тоже задать вопрос ты что никогда спагети-код написанный в стиле ассемблера, с максимальной длиной переменных и идентификаторов в две с половиной буквы и кучей к месту и не к месту меток, не разгребал? |
Сообщ.
#101
,
|
|
|
Цитата Wound @ А причем тут что я разгребал и то что мы обсуждаем? Я тебе могу тоже задать вопрос ты что никогда спагети-код написанный в стиле ассемблера, с максимальной длиной переменных и идентификаторов в две с половиной буквы и кучей к месту и не к месту меток, не разгребал? Да при том, что спагеттность зависит не от того, используешь ты goto или объекты, а от плохой структурированности архитектуры приложения. Попробуй добавить ещё 45 процессов и ветвлений (decision), и сгенерировать схему. Можешь приложить скриншот сюда — посмотрим, сколько там будет стрелок и их пересечений. |
Сообщ.
#102
,
|
|
|
Цитата korvin @ Попробуй добавить ещё 45 процессов и ветвлений (decision), и сгенерировать схему. Можешь приложить скриншот сюда — посмотрим. Ну во первых мне честно лень добавлять еще 45 процессов. Это тупая копипаста получится. Схема от этого особо не изменится, если ты заметил у меня возвращается там не просто строка, а поле класса, генератор построит примерно такую же блоксхему как на твоем скрине, т.к. каждый объект решения будет ссылаться на соответствующие процессы, единственное что там не будет писать Yes/No, но связи понятны будут. Вот пример того что сгенерировалось на том, что уже там есть: Добавлено Цитата korvin @ Да при том, что спагеттность зависит не от того, используешь ты goto или объекты, а от плохой структурированности архитектуры приложения. Как показывает практика и мой личный опыт, практически в 99% случаев код с goto - представляет из себя спагети-код. Добавлено Еще в Сях так или иначе goto можно оправдать когда надо вылететь из -за МКАДа куда то в конец функции на Cleanup, и то это не всегда оправдано. А уж в плюсах - этот goto нахрен не упал, ИМХО. |
Сообщ.
#103
,
|
|
|
Цитата Wound @ если ты заметил у меня возвращается там не просто строка, а поле класса И что? Цитата Wound @ Вот пример того что сгенерировалось на том, что уже там есть: Ужас какой. А вот эти *Context — они чему соответствуют на изначальной схеме? Судя по этой диаграмме все ProcessN обрывают выполнение программы. |
Сообщ.
#104
,
|
|
|
Цитата korvin @ И что? Ничего. Просто это означает что у меня из готовых классов, хоть даже их будет 50, проще будет востановить изначальную схему, чем у тебя с goto. Цитата korvin @ Ужас какой. А вот эти *Context — они чему соответствуют на изначальной схеме? Судя по этой диаграмме все ProcessN обрывают выполнение программы. Это базовая диаграмма на всякий шлак, не нужное можно выбросить и оставить только нужное. Расположить как тебе нравится. Ужас будет когда твой switch/goto кто то будет разгребать. Вот там да - будет ужас. А например, если выкинуть весь вспомогательный шлак и отфильтровать не нужное, то в сухом остатке останется вот такая схема: Добавлено На картинке это конечно херово показывать, но в IDE, я сразу могу ткнуть кнопку код, и тогда, когда я тыкну на ноду Decision, мне сразу откроется ее код, в котором идет условие. Т.е. я находу могу понять когда оно будет ссылаться на один процес, а когда на другой. Соответственно схема у меня уже готовая, даже разгребать ничего не нужно. Добавлено А теперь ты покажи, как ты будешь востанавливать изначальную блоксхему из всех этих goto, особенно в каких нибудь плюсах. Вангую щас полетят "аргументы" в стиле - гляну на код, закрою глаза и сразу в мозгу схема нарисуется |
Сообщ.
#105
,
|
|
|
Цитата Wound @ А теперь ты покажи, как ты будешь востанавливать изначальную блоксхему из всех этих goto, особенно в каких нибудь плюсах. Вангую щас полетят "аргументы" в стиле - гляну на код, закрою глаза и сразу в мозгу схема нарисуется А зачем мне «восстанавливать схему», если оно отображена в коде как есть? int main() { PROC_1 : process_1() ; if (decision_1()) goto PROC_4 ; else goto PROC_3 ; PROC_2 : process_2() ; if (decision_2()) goto PROC_3 ; else goto PROC_4 ; PROC_3 : process_3() ; if (decision_3()) goto PROC_2 ; else goto PROC_1 ; PROC_4 : process_4() ; if (decision_4()) goto EXIT ; else goto PROC_2 ; EXIT: return 0; } Странный ты, наплодил себе классов, теперь без сгенерированной диаграммы не можешь разобраться в коде ) |
Сообщ.
#106
,
|
|
|
Цитата korvin @ Судя по этой диаграмме все ProcessN обрывают выполнение программы. Вообще это показаны вызовы. Что откуда вызывается. У тебя на блок схеме(и в условии задачи так же) - решения(DecisionN) - в зависимости от результата(true/false), который сделал процесс, определяют какой процесс будет вызван следующим. Но сам процесс - никак не участвует в передаче управления кому либо, он лишь выполняет некие операции. Вот и получается что на схеме показаны вызовы от решений(Decision), а стрелки от процессов, всегда направлены на соответствующее процессу решение. Процес1 всегда ссылается на решени1, Процесс2 на решение2, процес3 на решение3 и т.д. Поэтому странно считать что ProcessN - что то там обрывает. Он не обрывает выполнение программы, он просто ничего не вызывает, и ни накого не ссылается, просто выполняет определенную операцию и все. Добавлено Цитата korvin @ А зачем мне «восстанавливать схему», если оно отображена в коде как есть? Во первых в твоем этом коде уже можно запутаться, ты помнишь свой аргумент про то, сколько человек может вещей в уме держать одновременно? У тебя тут как минимум 4 метки и 4 процесса, перемешанные в условиях, что уже ломает общую картину, а ведь это только 4 процесса! Если их тут будет даже не 50, а еще столько уже - это уже будет нечитаемое говно, даже с учетом твоего форматирования. Потом, то что ты метку, вызов функции, потом условный оператор вместе с телом зарядил в одну строчку - это вообще отдельный лол. Потом эту шнягу такой сохраняешь, а какая нибудь падла типа IDE, или еще какой нибудь штуки, неверно раздуплилась с табами в перемешку с пробелами, и получилась каша, снова форматируй. Потом ты взял эталонный спагети код, отформатировал его - чтоб визуально не наблюдать этот звиздец, и выдаешь это за ахеренное архитектурное решение. Но в нем даже при 4-рех процессах, уже глаза разбегаются! |
Сообщ.
#107
,
|
|
|
Majestio, кажись, я слегка погорячился, прованговав всем известный паттерн.
Цитата Wound @ Сосбсна, в этих случаях goto как раз и выручает. Я понимаю, что это не вполне интуитивно, но в исходном коде goto появился ровно по причине внезапно изменившегося ТЗ. Majestio вас некисло так потроллил, сформулировав ТЗ так, что у вас все вводные сразу на руках. Уверяю, в рамках его условия, если я и выложу код, хотя боюсь, к тому времени конкурс уже будет закрыт, там goro не будет. Суровая реальность когда система постоянно расширяется, и дополняется. Выдвигают новые условия, новые фичи и т.д. |
Сообщ.
#108
,
|
|
|
Цитата korvin @ Странный ты, наплодил себе классов, теперь без сгенерированной диаграммы не можешь разобраться в коде ) Нет, это ты странный. Я прекрасно в своем коде разбираюсь, это же ты зачем то захотел из кода получить диаграмму. Так я тебе ее сделал, даже не смотря в код. Хотя там по коду и так все ясно без всяких диаграм. Теперь когда я тебя попросил построить диаграмму, ты пишешь что тут и так все ясно. Но тут ничего не ясно, тут ты из лапши попытался сделать что то более менее читаемое, но все эти метки в перемешку с процессами и if'ами - лишь ломают общую картину. Добавлено Цитата Qraizer @ Сосбсна, в этих случаях goto как раз и выручает. Я понимаю, что это не вполне интуитивно, но в исходном коде goto появился ровно по причине внезапно изменившегося ТЗ. Majestio вас некисло так потроллил, сформулировав ТЗ так, что у вас все вводные сразу на руках. Уверяю, в рамках его условия, если я и выложу код, хотя боюсь, к тому времени конкурс уже будет закрыт, там goro не будет. Меня goto ниразу не выручал, потому что я им не пользуюсь, и даже там где возможно его можно было впендюлить - как правило всегда находятся более приемлемые решения. А вот когда на него натыкаешься - жгучую боль пониже спины он не редко вызывает, это да. |
Сообщ.
#109
,
|
|
|
Цитата Qraizer @ всем известный паттерн. Что за паттерн-то? |
Сообщ.
#110
,
|
|
|
Цитата Qraizer @ к тому времени конкурс уже будет закрыт, там goro не будет. korvin и Wound по красоте зарубились! Очень хочу дождаться участия ЫукпШ, OpenGL (но у него проблемсы), D_KEY, они ведь тут подбрасывали в начале идеи. Ну и вскроем пароли |
Сообщ.
#111
,
|
|
|
Цитата korvin @ если оно отображена в коде как есть? Вот так оно будет к слову выглядеть, если переписать по нормальному, а не бабочек рисовать кодом, которые потом еще попробуй пойди отладь: int main() { PROC_1 : process_1() ; if (decision_1()) goto PROC_4 ; else goto PROC_3 ; PROC_2 : process_2() ; if (decision_2()) goto PROC_3 ; else goto PROC_4 ; PROC_3 : process_3() ; if (decision_3()) goto PROC_2 ; else goto PROC_1 ; PROC_4 : process_4() ; if (decision_4()) goto EXIT; else goto PROC_2 ; EXIT: return 0; } Очень все ясно и понятно, решение просто космос. Добавлено Я тут даже чутка отформатировал отступы для меток, чтоб оно хоть как то полегче понималось. И это самый простейший случай! |
Сообщ.
#112
,
|
|
|
Спасибо korvin и Wonder за обсуждение - вы меня вдохновили на новую версию!
Wonder, отдельный респект - он мне в личку раскрыл свои исходники. И я подсмотрел у него некоторые профессиональные решения - но пришлось использовать оператор goto Прилагаю вторую версию, добавил изящества 80 лвл. Прикреплённый файлmajestio_goto.cpp.7z (1002 байт, скачиваний: 37) |
Сообщ.
#113
,
|
|
|
Цитата Wound @ которые потом еще попробуй пойди отладь Юнит-тесты? Не, не слышал. Цитата Wound @ Вот так оно будет к слову выглядеть, если переписать по нормальному Ты ничего не понял и всё испортил. |
Сообщ.
#114
,
|
|
|
Цитата korvin @ Юнит-тесты? Не, не слышал. Во первых причем тут юнитесты? У тебя будет чуть сложнее условие, ты в if'возьмешь провтыкаешь и вместо какого нибудь == напишешь присваивание(в плюсах как нехер делать), и потом сиди отлаживай своими юниттестами свою портянку, они тебя в этом случае не спасут. Ты там недавно мне втирал про стектрейсы с DI, откуда ты их мог видеть, если у тебя контраргумент против отладки - Юнит-Тесты? Я ж так понимаю ты никогда не отлаживался, все юнит-тестами Цитата korvin @ Ты ничего не понял и всё испортил. Нет, я прекрасно все понял. Ты взял эталонный спагетти-код, и просто захерачил 3 конструкции в одну строчку однотипно, чтоб этот трешак - более менее можно было понять. Даже у условных любителей goto, которые не брезгуют его применять, есть правила. И одно из них - прыгать можно на метку вперед, назад - это уже из серии говнокода, даже среди поклонников goto. А у тебя как раз тот случай, когда в зависимости от условия мы будем прыгать непойми куда, либо вперед, либо назад, за что собственно и критикуют goto, и предсказать выполнение программы, без ее отладки - зачастую затруднительно. Это даже затруднительно сделать глядя на твой код. Т.к. тут 4 метки, 4 условия(if/else), 8 прыжков, и код не тривиальный, т.к. ты несколько конструкций включая if/else в одну строку свернул(такое в основном студенты любят делать), что снижает читабельность и понимание кода. Так что кончай прикрывать этот спагетти-код, каким то сомнительными аргументами. Добавлено Все эти goto вполне себе могут быть оправданы в низкоуровневых яп , типа асма, да хоть те же си, где банально нет интструментов и смысла городить всякую архитектуру в стиле ООП или еще каком. |
Сообщ.
#115
,
|
|
|
Цитата Wound @ ты в if'возьмешь провтыкаешь и вместо какого нибудь == напишешь присваивание(в плюсах как нехер делать) Это не только статические анализаторы, это уже компиляторы давно отлавливают. Добавлено Цитата Majestio @ Очень хочу дождаться участия ... D_KEY, они ведь тут подбрасывали в начале идеи Ну поговорку про разговоры и мешки ты знаешь У меня сейчас полный завал дел и на работе и вообще в жизни, так что не смогу. |
Сообщ.
#116
,
|
|
|
Цитата D_KEY @ Это не только статические анализаторы, это уже компиляторы давно отлавливают Компилятор выдаёт варнинг, а не ошибку, т. к. это вполне себе законная конструкция. Поэтому не совсем понимаю к чему это? Мы общаемся про сферических коней в вакууме, и как по мне, форматирование кода, в стиле 5 конструкций в одну строчку, здорово усложняют не только отладку, но и чтение кода. |
Сообщ.
#117
,
|
|
|
Ещё одна, альтернативная, версия на Ocaml:
Прикреплённый файлmajestio_machine_alt.ml.zip (1006 байт, скачиваний: 41) И чисто функциональная версия на Haskell: Прикреплённый файлMajestio.hs.zip (895 байт, скачиваний: 37) Добавлено Цитата Wound @ Компилятор выдаёт варнинг, а не ошибку, т. к. это вполне себе законная конструкция. C/C++-проблемы. Заметь, ни один мой вариант не использует эти языки. |
Сообщ.
#118
,
|
|
|
Цитата korvin @ C/C++-проблемы. Заметь, ни один мой вариант, не использует эти языки. Да, в плюсах это наследие от Си. В тех же шарпах это не законная конструкция и будет ошибка компиляции |
Сообщ.
#119
,
|
|
|
Цитата Wound @ Компилятор выдаёт варнинг, а не ошибку Я без -Werror уже несколько лет не работаю. Впрочем, еще и статические анализаторы подключаю. |
Сообщ.
#120
,
|
|
|
Цитата D_KEY @ Я без -Werror уже несколько лет не работаю. Впрочем, еще и статические анализаторы подключаю. А те варнинги от которых в принципе избавится не возможно, ты давишь директивами #pragma Аргументы поперли просто жесть. Да какая разница с чем ты там работаешь или нет? Мы обсуждаем твою личность тут или чего? Я goto в принципе не использую, и никогда в жизни у меня даже не возникало ни желания, ни мысли его использовать на работе(разве что только в исследовательских целях, дома когда изучал ЯП). Значит все в мире должны перестать его тоже использовать? Я верно понял твою логику? Добавлено И вообще goto рудимент и пережиток прошлого. В тех языках например, где нет конструкции break в циклах for - его еще можно оправдать, а именно там он как бы и уместен, чтоб выпрыгивать досрочно из цикла, и то в тех же языках - его можно заменить while'ом, и никаких goto не нужно. А в высокоуровневых языках, я даже не могу представить где бы он мог бы понадобиться. Добавлено Разве что писать спагетти-код, и доказывать окружающим, что теперь я в этом вижу какую то блоксхему... |
Сообщ.
#121
,
|
|
|
Wound, я спорил с конкретным тезисом о том, что такую ошибку легко допустить в C++. Я с этим не согласен, потому что при нормальной организации работы, тебе даст по рукам уже компилятор.
Ни с чем другим я не спорил. |
Сообщ.
#122
,
|
|
|
Цитата D_KEY @ Wound, я спорил с конкретным тезисом о том, что такую ошибку легко допустить в C++. Я с этим не согласен, потому что при нормальной организации работы, тебе даст по рукам уже компилятор. Ни с чем другим я не спорил. Ты с этим не согласен - это твое субъективное мнение или объективное? Если это твое субъективное мнение, то ты прав, потому что ты там что то используешь, что в принципе тебя может от этого спасти(с другой стороны конструкции такого плана, которые могут встретится в каких нибудь сторонних библиотеках - нужно как то исключать?). Но кроме тебя - еще полно людей, которые не используют то, что ты там у себя используешь, и объективно - то что ты приводишь, это не аргумент. Хотя применительно к тебе - аргумент да. Но мы не тебя же обсуждаем. А то тебя послушать - так при нормальной организации работы, при нормальных знаниях ЯП, и тд. вообще будет все в шоколаде, но на практике - это не так. Добавлено Ну и к слову - я на такие баги натыкался, когда вместо сравнения, было присваивание, причем натыкался случайно, и не один раз, в древнем, как говно мамонта коде, на тех же плюсах. |
Сообщ.
#123
,
|
|
|
Ну что, уважаемые, вижу - все кто мог участвовать, уже смогли. Перечислю тех, кто хотел бы поучаствовать, но не с мог по причине запарок на работе, или отсутствием времени по иным причинам:
Про остальных - не знаю. Поэтому, считаю, что пора "вскрываться" Пароли к архивам Победила дружба! И спасибо всем за участие, особенно порадовал korvin многообразием вариантов. Што-то смотрю мне OCaml немного приглянулся Ну а выводы, надеюсь, всем понятны - никуда с помощью GOTO "впрыгивать" не нужно, равно как и городить многовложенных циклов, коль впереди маячат долгие пересчеты с сохранением состояний. Будет просто супер, если каждый из участвующих в отдельном сообщении соберет все свои варианты, но уже в текстовом виде под спойлер, а рядом выложит ссылку исполнения в онлайн компиляторе. Peace! |
Сообщ.
#124
,
|
|
|
Вариант 1 (С++, без GOTO) Ссылка на онлайн-исполнение
Скрытый текст #include <iostream> #include <vector> #include <algorithm> using namespace std; std::vector<int> Arr {0,0,0,0}; std::size_t Step = 0; std::size_t State = 0; void prn(); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool process1() { Step++; Arr[0]++; int Summ = 0; std::for_each(Arr.begin(), Arr.end(), [&](int &n){Summ += n;}); return Summ % 2 == 0; } bool process2() { Step++; Arr[1] += Arr[3] + 1; int Summ = 0; std::for_each(Arr.begin(), Arr.end(), [&](int &n){Summ += n;}); return Summ % 7 > 0; } bool process3() { Step++; Arr[2] += ((Arr[0]+Arr[1]) % 3 == 0) ? 0 : 7; bool Flag = true; std::for_each(Arr.begin(), Arr.end(), [&](int &n){if (n % 2 >0) Flag = false;}); return Flag; } bool process4() { Step++; Arr[3] += ((Arr[0]+Arr[2]) % 5 == 4) ? 0 : 1; int Summ = 0; std::for_each(Arr.begin(), Arr.end(), [&](int &n){Summ += n;}); return Summ > 24; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void load(int pass) { if (pass == 1) { State = 1; prn(); } else { State = 4; Arr = {3,3,14,1}; Step = 9; prn(); } return; } void prn() { std::cout << "Step: " << Step << "\t" << "Proc: " << State << "\t" << Arr[0] << "\t" << Arr[1] << "\t" << Arr[2] << "\t" << Arr[3] << "\n"; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - int main() { for (int i=1; i<3; i++) { load(i); bool cycle = true; while (cycle) { switch (State) { case 1: State = (process1()) ? 4:3; prn(); break; case 2: State = (process2()) ? 3:4; prn(); break; case 3: State = (process3()) ? 2:1; prn(); break; case 4: State = (process4()) ? 5:2; prn(); break; default: cycle = false; break; } } std::cout << "----------------------------------------------------------------\n"; } return 0; } Вариант 2 (С++, с прекрасным оператором GOTO) Ссылка на онлайн-исполнение Скрытый текст #include <functional> #include <algorithm> #include <iterator> #include <iostream> #include <numeric> #include <vector> int main() { int Idx = 0; int Step = 0; bool Continue = true; std::vector<int>A = {0, 0, 0, 0}; std::vector<std::vector<int>> Matrix = {{3,2},{2,3},{1,0},{4,1},{4,4}}; auto P1 = [&]() -> void { A[0]++; }; auto P2 = [&]() -> void { A[1] += A[3] + 1; }; auto P3 = [&]() -> void { A[2] += ((A[0] + A[1]) % 3 == 0) ? 0 : 7; }; auto P4 = [&]() -> void { A[3] += ((A[0] + A[2]) % 5 == 4) ? 0 : 1; }; auto Fp = [&]() -> void { }; auto D1 = [&]() -> bool { return std::accumulate(A.begin(), A.end(), 0) % 2 == 0; }; auto D2 = [&]() -> bool { return std::accumulate(A.begin(), A.end(), 0) % 7 > 0; }; auto D3 = [&]() -> bool { return std::find_if(A.begin(), A.end(), [](int i){ return i % 2 > 0; }) == A.end(); }; auto D4 = [&]() -> bool { return std::accumulate(A.begin(), A.end(), 0) > 24; }; auto Fd = [&]() -> bool { Continue = false; return false; }; auto Print = [&]() -> void { std::cout << "Step: " << Step << "\tProc: " << (Idx+1) << "\t"; std::copy(A.begin(), A.end(), std::ostream_iterator<int>(std::cout, "\t")); std::cout << std::endl; }; using Process = std::function<void()>; using Decision = std::function<bool()>; std::vector<Process> P = {P1, P2, P3, P4, Fp}; std::vector<Decision> D = {D1, D2, D3, D4, Fd}; // Проход с начала и до конца while(Continue) { Print(); P[Idx](); Idx = Matrix[Idx][(D[Idx]()) ? 0:1]; Step++; } std::cout << "----------------------------------------------------------------\n"; // Эмуляция восстановления в точке перед Proc-4 и продолжение Continue = true; Step = 9; Idx = 3; A = {3, 3 ,14, 1}; while(Continue) { Print(); P[Idx](); Idx = Matrix[Idx][(D[Idx]()) ? 0:1]; Step++; } std::cout << "----------------------------------------------------------------\n"; // использование прекрасного оператора GOTO! label: if (true == false) goto label; return 0; } |
Сообщ.
#125
,
|
|
|
Цитата Majestio @ Будет просто супер, если каждый из участвующих в отдельном сообщении соберет все свои варианты, но уже в текстовом виде под спойлер, а рядом выложит ссылку исполнения в онлайн компиляторе. Онлайн -> https://rextester.com/PKDT40835 Код using System; using System.Collections.Generic; using System.Linq; using Challenge.Interfaces; //! Interfaces namespace Challenge.Interfaces { public interface IDecisionHandler { string HandleDecision(bool decision); } public interface IProcess { void Execute(List<int> values); bool Decision(List<int> values); } } namespace Challenge { //! DecisionhandlerContext public class DecisionHandlerContext { private Dictionary<string, IDecisionHandler> _handlers; public DecisionHandlerContext() { _handlers = new Dictionary<string, IDecisionHandler>(); } public void RegisterHandler(string processName, IDecisionHandler decisionHandler) { if (String.IsNullOrEmpty(processName) || decisionHandler == null) throw new Exception("Argument can't be null"); _handlers.Add(processName, decisionHandler); } public string HandleDecisionAndGetNextStep(string processName, bool decision) { if (String.IsNullOrEmpty(processName)) throw new Exception("Invalid arguments"); if(_handlers.TryGetValue(processName, out var handler)) { return handler.HandleDecision(decision); } throw new Exception("Handle error"); } } public class Process1DecisionHandler : IDecisionHandler { public string HandleDecision(bool decision) { if(decision) return Process4.ProcessName; return Process3.ProcessName; } } public class Process2DecisionHandler : IDecisionHandler { public string HandleDecision(bool decision) { if (decision) return Process3.ProcessName; return Process4.ProcessName; } } public class Process3DecisionHandler : IDecisionHandler { public string HandleDecision(bool decision) { if (decision) return Process2.ProcessName; return Process1.ProcessName; } } public class Process4DecisionHandler : IDecisionHandler { public string HandleDecision(bool decision) { if (decision) return Process5.ProcessName; return Process2.ProcessName; } } public class Process5DecisionHandler : IDecisionHandler { public string HandleDecision(bool decision) { return String.Empty; } } //! Process1 public class Process1 : IProcess { private const int MaxSizeValues = 4; public static string ProcessName => "1"; public void Execute(List<int> values) { if (values == null || values.Count > MaxSizeValues) throw new InvalidOperationException("List of values is empty..."); ++values[0]; } public bool Decision(List<int> values) { return (values.Sum() % 2) == 0; } } //! Process2 public class Process2 : IProcess { private const int MaxSizeValues = 4; public static string ProcessName => "2"; public bool Decision(List<int> values) { return (values.Sum() % 7) > 0; } public void Execute(List<int> values) { if (values == null || values.Count > MaxSizeValues) throw new InvalidOperationException("List of values is empty..."); values[1] += values[3] + 1; } } //! Process3 public class Process3 : IProcess { private const int MaxSizeValues = 4; public static string ProcessName => "3"; public bool Decision(List<int> values) { var existOdd = values.Any(e => e % 2 != 0); return !existOdd; } public void Execute(List<int> values) { if (values == null || values.Count > MaxSizeValues) throw new InvalidOperationException("List of values is empty..."); if ( (values[0] + values[1]) % 3 > 0 ) values[2] += 7; } } //! Process4 public class Process4 : IProcess { private const int MaxSizeValues = 4; public static string ProcessName => "4"; public bool Decision(List<int> values) { return values.Sum() > 24; } public void Execute(List<int> values) { if (values == null || values.Count > MaxSizeValues) throw new InvalidOperationException("List of values is empty..."); if ((values[0] + values[2]) % 5 != 4) ++values[3]; } } //! Process5 public class Process5 : IProcess { public static string ProcessName => "5"; public bool Decision(List<int> values) { return true; } public void Execute(List<int> values) { } } //! ProcessContext public class ProcessContext { private Dictionary<string, IProcess> _processes; private DecisionHandlerContext _handlerContext; public ProcessContext() { _processes = new Dictionary<string, IProcess>(); _handlerContext = new DecisionHandlerContext(); } public ProcessContext AddProcess(string proccesName, IProcess process, IDecisionHandler handler) { if (String.IsNullOrEmpty(proccesName) || process == null) throw new Exception("Argument can't be null"); _processes.Add(proccesName, process); _handlerContext.RegisterHandler(proccesName, handler); return this; } public bool RunProcessAndCheckDecision(string processName, List<int> values) { if(_processes.TryGetValue(processName, out var proc)) { if (proc == null) throw new Exception($"Process with name '{processName}' is null."); proc.Execute(values); return proc.Decision(values); } throw new Exception($"Process with name '{processName}' not found."); } public void RunAllProcesses(string startProcessName, List<int> values) { string currentProcessName = startProcessName; int step = 0; while (true) { Console.WriteLine($"Step {step}\t Proc: {currentProcessName}\t {String.Join(" ", values)}"); var result = RunProcessAndCheckDecision(currentProcessName, values); currentProcessName = _handlerContext.HandleDecisionAndGetNextStep(currentProcessName, result); if (String.IsNullOrEmpty(currentProcessName)) break; ++step; } } } //! Programm public class Program { static void Main(string[] args) { try { List<int> values = new List<int> { 0, 0, 0, 0 }; ProcessContext processContext = new ProcessContext(); processContext .AddProcess(Process1.ProcessName, new Process1(), new Process1DecisionHandler()) .AddProcess(Process2.ProcessName, new Process2(), new Process2DecisionHandler()) .AddProcess(Process3.ProcessName, new Process3(), new Process3DecisionHandler()) .AddProcess(Process4.ProcessName, new Process4(), new Process4DecisionHandler()) .AddProcess(Process5.ProcessName, new Process5(), new Process5DecisionHandler()); processContext.RunAllProcesses(Process1.ProcessName, values); Console.WriteLine("--------------------------"); List<int> savedValues = new List<int> { 3, 3, 14, 1 }; processContext.RunAllProcesses(Process4.ProcessName, savedValues); } catch(Exception error) { Console.WriteLine(error.Message); } } } } |
Сообщ.
#126
,
|
|
|
Ocaml #1 module Memory = struct type cells = int array let size = 4 let alloc () = Array.make size 0 end module type CPU = sig type process = Memory.cells -> unit val process_1 : process val process_2 : process val process_3 : process val process_4 : process type decision = Memory.cells -> bool val decision_1 : decision val decision_2 : decision val decision_3 : decision val decision_4 : decision end module Machine (CPU : CPU) = struct open CPU type cpu_state = | Process_1 | Process_2 | Process_3 | Process_4 | Stop type machine = { mutable cpu_state : cpu_state ; mutable cpu_counter : int ; memory : Memory.cells } let new_machine ?(cpu_state=Process_1) ?(cpu_counter=0) ?(memory_init=[||]) () = let memory = Memory.alloc () in for i = 0 to min (Memory.size - 1) (Array.length memory_init - 1) do memory.(i) <- memory_init.(i) done ; { cpu_state = cpu_state ; cpu_counter = cpu_counter ; memory = memory } let cpu process machine = process machine.memory ; { machine with cpu_counter = machine.cpu_counter + 1 } let branch decision machine = (machine, decision machine.memory) let yes y ~no (machine, flag) = { machine with cpu_state = if flag then y else no } let ( ==> ) = ( |> ) let rec run machine = describe machine ; match machine.cpu_state with | Process_1 -> machine ==> cpu process_1 ==> branch decision_1 ==> yes Process_4 ~no: Process_3 ==> run | Process_2 -> machine ==> cpu process_2 ==> branch decision_2 ==> yes Process_3 ~no: Process_4 ==> run | Process_3 -> machine ==> cpu process_3 ==> branch decision_3 ==> yes Process_2 ~no: Process_1 ==> run | Process_4 -> machine ==> cpu process_4 ==> branch decision_4 ==> yes Stop ~no: Process_2 ==> run | Stop -> () and describe { cpu_state ; cpu_counter ; memory } = Printf.printf "Step: %2d %-8s %s\n" cpu_counter (state_string cpu_state) (mem_string memory) and state_string state = match state with | Process_1 -> "Proc: 1" | Process_2 -> "Proc: 2" | Process_3 -> "Proc: 3" | Process_4 -> "Proc: 4" | Stop -> "Stop " and mem_string mem = let str = ref "" in for i = 0 to Memory.size - 1 do str := !str ^ Printf.sprintf " %3d" mem.(i) done ; !str end module Majestio_CPU : CPU = struct type process = Memory.cells -> unit let process_1 mem = mem.(0) <- mem.(0) + 1 let process_2 mem = mem.(1) <- mem.(1) + mem.(3) + 1 let process_3 mem = let sum_0_1 = mem.(0) + mem.(1) in if sum_0_1 mod 3 <> 0 then mem.(2) <- mem.(2) + 7 let process_4 mem = let sum_0_2 = mem.(0) + mem.(2) in if sum_0_2 mod 5 <> 4 then mem.(3) <- mem.(3) + 1 type decision = Memory.cells -> bool let sum = Array.fold_left (+) 0 let decision_1 mem = sum mem mod 2 = 0 let decision_2 mem = sum mem mod 7 > 0 let decision_3 mem = let is_even x = x mod 2 = 0 in Array.for_all is_even mem let decision_4 mem = sum mem > 24 end let main () = let module M = Machine (Majestio_CPU) in M.new_machine () |> M.run ; print_endline "----------------------------------------------------------------" ; M.new_machine ~cpu_state: M.Process_4 ~cpu_counter: 9 ~memory_init: [| 3; 3; 14; 1 |] () |> M.run ; print_endline "----------------------------------------------------------------" let () = main () — https://rextester.com/YFHF41308 Ocaml #2 module type Machine = sig type data = int array val init : ?data:data -> unit -> unit val dump : unit -> data type process = unit -> unit val process_1 : process val process_2 : process val process_3 : process val process_4 : process type decision = unit -> bool val decision_1 : decision val decision_2 : decision val decision_3 : decision val decision_4 : decision end module Program (M : Machine) = struct type state = | Proc_1 | Proc_2 | Proc_3 | Proc_4 | Stop let ( --> ) = ( |> ) let yes y ~no:n decision = if decision then y else n let rec run count state = log_state count state ; let loop = run (count + 1) in match state with | Proc_1 -> () --> M.process_1 --> M.decision_1 --> yes Proc_4 ~no:Proc_3 --> loop | Proc_2 -> () --> M.process_2 --> M.decision_2 --> yes Proc_3 ~no:Proc_4 --> loop | Proc_3 -> () --> M.process_3 --> M.decision_3 --> yes Proc_2 ~no:Proc_1 --> loop | Proc_4 -> () --> M.process_4 --> M.decision_4 --> yes Stop ~no:Proc_2 --> loop | Stop -> () and log_state count state = let data = M.dump () in Printf.printf "Step: %2d %s %s\n" count (state_string state) (data_string data) and state_string s = match s with | Proc_1 -> "Process 1" | Proc_2 -> "Process 2" | Proc_3 -> "Process 3" | Proc_4 -> "Process 4" | Stop -> "Stop " and data_string data = data --> Array.map (Printf.sprintf " %2d") --> Array.to_list --> String.concat " " end module Majestio_machine : Machine = struct let size = 4 let cell = Array.make size 0 type data = int array let init ?(data=[||]) () = let n = min size (Array.length data) in if n = 0 then Array.fill cell 0 size 0 else Array.blit data 0 cell 0 n let dump () = Array.copy cell type process = unit -> unit let process_1 () = cell.(0) <- cell.(0) + 1 let process_2 () = cell.(1) <- cell.(1) + cell.(3) + 1 let process_3 () = let sum_0_1 = cell.(0) + cell.(1) in if sum_0_1 mod 3 <> 0 then cell.(2) <- cell.(2) + 7 let process_4 () = let sum_0_2 = cell.(0) + cell.(2) in if sum_0_2 mod 5 <> 4 then cell.(3) <- cell.(3) + 1 type decision = unit -> bool let sum = Array.fold_left (+) 0 let decision_1 () = sum cell mod 2 = 0 let decision_2 () = sum cell mod 7 > 0 let decision_3 () = let is_even x = x mod 2 = 0 in Array.for_all is_even cell let decision_4 () = sum cell > 24 end let main () = let module Machine = Majestio_machine in let module Program = Program (Machine) in Machine.init () ; Program.run 1 Program.Proc_1 ; print_endline "----------------------------------------------------------------" ; Machine.init ~data:[| 3; 3; 14; 1 |] () ; Program.run 9 Program.Proc_4 ; print_endline "----------------------------------------------------------------" ; print_endline "done" let () = main () — https://rextester.com/WPDD51431 Ocaml #3 module Machine = struct type memory = int array type process = memory -> unit type decision = memory -> bool type cpu = { processes : process array ; decisions : decision array } type program = (int * int * int) array let majestio : program = [| 1, 4, 3; 2, 3, 4; 3, 2, 1; 4, 5, 2 |] let rec run ?(count = 0) ?(proc = 1) cpu mem program = log_state count proc mem ; let proc = proc - 1 in if 0 <= proc && proc < Array.length program then begin let dec, yes, no = program .(proc) in let process = cpu.processes.(proc) in let decision = cpu.decisions.(dec-1) in process mem ; let next = if decision mem then yes else no in run ~count:(count+1) ~proc:next cpu mem program end and log_state count process mem = let mem = mem |> Array.map (Printf.sprintf "%2d") |> Array.to_list |> String.concat " " in Printf.printf "Step: %2d Proc: %d %s\n" count process mem end module Majestio = struct let process_1 cell = cell.(0) <- cell.(0) + 1 let process_2 cell = cell.(1) <- cell.(1) + cell.(3) + 1 let process_3 cell = let sum_0_1 = cell.(0) + cell.(1) in if sum_0_1 mod 3 <> 0 then cell.(2) <- cell.(2) + 7 let process_4 cell = let sum_0_2 = cell.(0) + cell.(2) in if sum_0_2 mod 5 <> 4 then cell.(3) <- cell.(3) + 1 let sum = Array.fold_left (+) 0 let decision_1 cell = sum cell mod 2 = 0 let decision_2 cell = sum cell mod 7 > 0 let decision_3 cell = let is_even x = x mod 2 = 0 in Array.for_all is_even cell let decision_4 cell = sum cell > 24 let cpu = { Machine. processes = [| process_1; process_2; process_3; process_4 |] ; decisions = [| decision_1; decision_2; decision_3; decision_4 |] } let program = [| 1, 4, 3; 2, 3, 4; 3, 2, 1; 4, 5, 2 |] end let main () = Machine.run Majestio.cpu [| 0; 0; 0; 0 |] Majestio.program ; print_endline "----------------------------------------------------------------" ; Machine.run ~count:9 ~proc:4 Majestio.cpu [| 3; 3; 14; 1 |] Majestio.program ; print_endline "----------------------------------------------------------------" let () = main () — https://rextester.com/ARZ32340 Racket #lang racket (define (sum v) (for/sum ((x (in-vector v))) x)) (define (cell c1 c2 c3 c4) (vector c1 c2 c3 c4)) (define (mod x y) (remainder x y)) (define (div-by? n v) (zero? (mod v n))) (define (all v predicate?) (for/and ((x (in-vector v))) (predicate? x))) (define (are-even? x) (zero? (mod x 2))) (define (f l n #:left? (left? #f)) (let* ((s (format "~a" n)) (m (string-length s))) (cond ((>= m l) s) (left? (string-append s (make-string (- l m) #\Space))) (else (string-append (make-string (- l m) #\Space) s))))) (define-syntax-rule (state st) 'st) (define (counter c) c) (define-syntax define-machine (syntax-rules (state -> -- < > yes no) ((_ (name (counter counter-init) (state state-init) (cell c1 c2 c3 c4)) { -> [ proc-in action ] -> < post-cond > -- yes -> yes-proc-out -- no -> no-proc-out } ... ) (define (name (counter counter-init) (state 'state-init) (cells #(c1 c2 c3 c4))) (define cs (vector-copy cells)) (define-syntax cell (syntax-rules (<- ->) ((_) cs) ((_ i) (vector-ref cs i)) ((_ i <- op . vs) (vector-set! cs i (op (cell i) . vs))) ((_ op -> predicate?) (let ((op (op cs))) predicate?)))) (define (proc-in) action (if post-cond 'yes-proc-out 'no-proc-out)) ... (let loop ((state state) (counter counter)) (printf "Step: ~a ~a ~a ~a ~a ~a~n" (f 2 counter) (f 10 state #:left? #t) (f 2 (cell 0)) (f 2 (cell 1)) (f 2 (cell 2)) (f 2 (cell 3))) (case state ((proc-in) (loop (proc-in) (+ counter 1))) ... (else 'done))))))) (define-machine (majestio-machine (counter 0) (state process-1) (cell 0 0 0 0)) { -> [ process-1 (cell 0 <- + 1) ] -> < (cell sum -> (div-by? 2 sum)) > -- yes -> process-4 -- no -> process-3 } { -> [ process-2 (cell 1 <- + (cell 3) 1) ] -> < (cell sum -> (positive? (mod sum 7))) > -- yes -> process-3 -- no -> process-4 } { -> [ process-3 (unless (div-by? 3 (+ (cell 0) (cell 1))) (cell 2 <- + 7)) ] -> < (all (cell) are-even?) > -- yes -> process-2 -- no -> process-1 } { -> [ process-4 (let ((c0+c2 (+ (cell 0) (cell 2)))) (unless (= (mod c0+c2 5) 4) (cell 3 <- + 1))) ] -> < (cell sum -> (> sum 24)) > -- yes -> stop -- no -> process-2 } ) (define (main) (majestio-machine) (displayln "----------------------------------------------------------------") (majestio-machine (counter 9) (state process-4) (cell 3 3 14 1)) (displayln "----------------------------------------------------------------")) (main) — https://onecompiler.com/racket/3ygbcbxe2 Haskell module Main (main) where main :: IO () main = do machine (Run 0 Process 1 [0, 0, 0, 0]) putStrLn "----------------------------------------------------------------" machine (Run 9 Process 4 [3, 3, 14, 1]) putStrLn "----------------------------------------------------------------" ---------------------------------------------------------------- data CmdType = Process | Decision deriving (Show) type CmdAddr = Int type Counter = Int type Memory = [Int] data State = Run Counter CmdType CmdAddr Memory | Stop | Fail String instance Show State where show Stop = "stop" show (Fail err) = "failure: " ++ err show (Run c Process i cells) = "Step: " ++ align2 c ++ " Proc: " ++ show i ++ " " ++ showCells cells where showCells [] = "" showCells (cell:rest) = " " ++ align2 cell ++ showCells rest align2 n | n < 10 = " " ++ show n | otherwise = show n show (Run c t i cells) = "Step " ++ show c ++ " " ++ show t ++ " " ++ show i ++ " " ++ show cells ---------------------------------------------------------------- cpu :: State -> State cpu (Run c Process 1 [c0, c1, c2, c3]) = Run (c+1) Decision 1 [c0+1, c1, c2, c3] cpu (Run c Decision 1 cells) | even (sum cells) = Run c Process 4 cells | otherwise = Run c Process 3 cells cpu (Run c Process 2 [c0, c1, c2, c3]) = Run (c+1) Decision 2 [c0, c1+c3+1, c2, c3] cpu (Run c Decision 2 cells) | sum cells `mod` 7 > 0 = Run c Process 3 cells | otherwise = Run c Process 4 cells cpu (Run c Process 3 [c0, c1, c2, c3]) | (c0+c1) `mod` 3 /= 0 = Run (c+1) Decision 3 [c0, c1, c2+7, c3] | otherwise = Run (c+1) Decision 3 [c0, c1, c2, c3] cpu (Run c Decision 3 cells) | all even cells = Run c Process 2 cells | otherwise = Run c Process 1 cells cpu (Run c Process 4 [c0, c1, c2, c3]) | (c0+c2) `mod` 5 /= 4 = Run (c+1) Decision 4 [c0, c1, c2, c3+1] | otherwise = Run (c+1) Decision 4 [c0, c1, c2, c3] cpu (Run c Decision 4 cells) | sum cells > 24 = Run c Process 5 cells | otherwise = Run c Process 2 cells cpu (Run _ Process 5 _) = Stop cpu Stop = Stop cpu (Fail err) = Fail err cpu state = Fail (show state) ---------------------------------------------------------------- machine :: State -> IO () machine Stop = return () machine s@(Fail _) = print s machine s@(Run _ Process _ _) = print s >> machine (cpu s) machine s = machine (cpu s) — https://rextester.com/EQGX38142 Go package main import ( "context" "fmt" ) var ( process1 = make(chan int) process2 = make(chan int) process3 = make(chan int) process4 = make(chan int) stop = make(chan int) cell = make(Cells, 4) ) func main() { var ctx = context.Background() robot(ctx) start(0, process1, 0, 0, 0, 0) start(9, process4, 3, 3, 14, 1) } func robot(ctx context.Context) { go Block{ Name: "Proc: 1", Receive: process1, Process: Process1, Decision: Decision1, IfYes: process4, IfNo: process3, }.Connect(ctx) go Block{ Name: "Proc: 2", Receive: process2, Process: Process2, Decision: Decision2, IfYes: process3, IfNo: process4, }.Connect(ctx) go Block{ Name: "Proc: 3", Receive: process3, Process: Process3, Decision: Decision3, IfYes: process2, IfNo: process1, }.Connect(ctx) go Block{ Name: "Proc: 4", Receive: process4, Process: Process4, Decision: Decision4, IfYes: stop, IfNo: process2, }.Connect(ctx) } func start(counter int, process chan<- int, c1, c2, c3, c4 int) { cell[0], cell[1], cell[2], cell[3] = c1, c2, c3, c4 process <- counter counter = <-stop dump(counter, "Stop") fmt.Println("----------------------------------------------------------------") } type ( Cells []int Block struct { Name string Receive <-chan int Process Process Decision Decision IfYes chan<- int IfNo chan<- int } Process func() Decision func() bool ) func (b Block) Connect(ctx context.Context) { for { select { case <-ctx.Done(): // may save here case counter := <-b.Receive: dump(counter, b.Name) counter++ b.Process() if b.Decision() { b.yes(ctx, counter) } else { b.no(ctx, counter) } } } } func (b Block) yes(ctx context.Context, value int) { send(ctx, b.IfYes, value) } func (b Block) no(ctx context.Context, value int) { send(ctx, b.IfNo, value) } /* ---------------------------------------------------------------- */ func Process1() { cell[0]++ } func Process2() { cell[1] += cell[3] + 1 } func Process3() { if (cell[0] + cell[1])%3 != 0 { cell[2] += 7 } } func Process4() { if (cell[0] + cell[2])%5 != 4 { cell[3] += 1 } } /* ---------------------------------------------------------------- */ func Decision1() bool { return cell.sum()%2 == 0 } func Decision2() bool { return cell.sum()%7 > 0 } func Decision3() bool { return cell.areAllEven() } func Decision4() bool { return cell.sum() > 24 } /* ---------------------------------------------------------------- */ func (xs Cells) sum() (s int) { for _, x := range xs { s += x } return s } func (xs Cells) areAllEven() bool { for _, x := range xs { if x%2 != 0 { return false } } return true } func send(ctx context.Context, ch chan<- int, v int) { select { case ch <- v: case <-ctx.Done(): } } func dump(c int, p string) { fmt.Printf("Step: %2d %-10s %2d %2d %2d %2d\n", c, p, cell[0], cell[1], cell[2], cell[3]) } — https://go.dev/play/p/2AZ1MAlUaOp Common Lisp (defparameter *data* (vector 0 0 0 0)) (defun run-machine (&key (state 1) (count 0)) (labels ((dump (state) (format t "Step: ~2d Proc: ~2d ~{ ~2d~}~%" count state (coerce *data* 'list)) (incf count))) (tagbody (case state (1 (go :proc-1)) (2 (go :proc-2)) (3 (go :proc-3)) (4 (go :proc-4)) (t (go :stop))) :proc-1 (dump 1) (process-1) (if (decision-1) (go :proc-4) (go :proc-3)) :proc-2 (dump 2) (process-2) (if (decision-2) (go :proc-3) (go :proc-4)) :proc-3 (dump 3) (process-3) (if (decision-3) (go :proc-2) (go :proc-1)) :proc-4 (dump 4) (process-4) (if (decision-4) (go :stop) (go :proc-2)) :stop (dump 5)))) (defun process-1 () (incf (cell 0))) (defun process-2 () (incf (cell 1) (+ (cell 3) 1))) (defun process-3 () (unless (divisible (sum-cells 0 1) :by 3) (incf (cell 2) 7))) (defun process-4 () (unless (= 4 (rem (sum-cells 0 2) 5)) (incf (cell 3)))) (defun decision-1 () (divisible (cells-sum) :by 2)) (defun decision-2 () (plusp (rem (cells-sum) 7))) (defun decision-3 () (all-cells #'evenp)) (defun decision-4 () (> (cells-sum) 24)) ;------------------------------------------------ (defun cell (i) (aref *data* i)) (defun (setf cell) (v i) (setf (aref *data* i) v)) (defun divisible (v &key by) (zerop (rem v by))) (defun sum-cells (&rest indexes) (loop for i in indexes summing (cell i))) (defun cells-sum () (loop for x across *data* summing x)) (defun all-cells (predicate) (loop for x across *data* unless (funcall predicate x) do (return-from all-cells nil)) t) ;------------------------------------------------ (defun main () (run-machine) (format t "----------------------------------------------------------------~%") (let ((*data* (vector 3 3 14 1))) (run-machine :count 9 :state 4)) (format t "----------------------------------------------------------------~%")) (main) — https://rextester.com/RRI70257 |
Сообщ.
#127
,
|
|
|
Цитата Majestio @ label: if (true == false) goto label; Добавлено Цитата Majestio @ Вариант 1 (С++, без GOTO) Видишь, ты даже в синтетической задаче не нашел применение этому оператору. |
Сообщ.
#128
,
|
|
|
Ну тогда и я вскроюсь. В главном цикле приложения: конечный автомат для решений и переходов + сериализация для хранения состояний. Сохранение/восстановление тоже состояния автомата. Всё.
|
Сообщ.
#129
,
|
|
|
Цитата Wound @ Видишь, ты даже в синтетической задаче не нашел применение этому оператору. Ну в моем первом варианте - использование switch-case только это маскирует формально, а по сути - тот же if-goto, только вид сбоку |
Сообщ.
#130
,
|
|
|
Цитата Qraizer @ конечный автомат Гм, тогда к чему было: Цитата Qraizer @ кажись, я слегка погорячился, прованговав всем известный паттерн. ? Или ты про конкретно этот паттерн? |
Сообщ.
#131
,
|
|
|
Цитата korvin @ Или ты про конкретно этот паттерн? Скорее он про switch в цикле, ИМХО. Добавлено Я так предполагаю что то типа этого: http://www.devexp.ru/2011/02/konechnye-avtomaty-v-c/ Хотя могу ошибаться. В плюсах я только про такие автоматы слышал. |
Сообщ.
#132
,
|
|
|
Цитата Qraizer @ Ну тогда и я вскроюсь. В главном цикле приложения: конечный автомат для решений и переходов + сериализация для хранения состояний. Сохранение/восстановление тоже состояния автомата. Всё. Да, у всех один и тот же подход. А вот при постановке задачи "пересчеты-с-сохранением" - многовложенные циклы в 17 раз хуже использования прекрасного оператора GOTO |
Сообщ.
#133
,
|
|
|
Цитата Majestio @ А вот при постановке задачи "пересчеты-с-сохранением" - многовложенные циклы в 17 раз хуже использования прекрасного оператора GOTO Ты нигде его не использовал свой goto. Твой ивент - профанация. Я почти неделю ждал решения на goto, чтоб его обосрать и где оно? Только korvin, то что у тебя на языке было, написал сегодня или вчера кодом. И все. Короче blah-blah-blah... goto, goto, и где он этот goto в твоих исходниках? Я не вижу его. Добавлено Я тебе больше скажу - давай расширим ивент до 50 процессов. Только мне лень придумывать им решения. Я хочу посмотреть как распухнут твои исходники на плюсах. |
Сообщ.
#134
,
|
|
|
Цитата Wound @ Я хочу посмотреть как распухнут твои исходники на плюсах. На одно распухание: 2 лямбды + 1 пара значений в таблицу переходов Добавлено Цитата Wound @ Я почти неделю ждал решения на goto, чтоб его обосрать и где оно? Прости!!! |
Сообщ.
#135
,
|
|
|
Цитата Majestio @ На одно распухание: 2 лямбды + 1 пара значений в таблицу переходов Именно, оно уже выглядит пугающим. Когда там будет 100 лямбд - то уже будет на что посмотреть. |
Сообщ.
#136
,
|
|
|
Цитата Wound @ Именно, оно уже выглядит пугающим. Когда там будет 100 лямбд - то уже будет на что посмотреть. Не, ну можно 100 классов объявить вместо лямбд, как два пальца об асфальт |
Сообщ.
#137
,
|
|
|
Цитата Majestio @ Не, ну можно 100 классов объявить вместо лямбд, как два пальца об асфальт Фишка в том, что даже если будут 100 классов - они будут изолированы, и представлять собой конечную сущность, их можно оттестировать по отдельности например, представь что 100 человек написало по 1 классу. А с лямбдами сложнее выходит. Плюс лямбды твои, они у тебя гвоздями прибиты к переменной, объявленной в функции main -> A, а это очень херово. Добавлено Короче как по мне - даже первый твой вариант без GOTO, выигрывает по всем параметрам второй варинт с GOTO. И по читабельности кода, и по сопровождению. Потому что первый вариант можно и оттестировать, и расшрить без особых проблем. Второй вариант(который на лямбдах) - как по мне трешак, который придется полностью переписывать, если чуть условия усложнить. |
Сообщ.
#138
,
|
|
|
Цитата korvin @ Я большей частью имел в виду хранение состояния через сериализацию. Как оно у кого будет реализовано, как бы без разницы. Я вот думал забабахать boost:serialisation, шоб вам тут мёдом не. Цитата Qraizer @ конечный автомат Гм, тогда к чему было: Цитата Qraizer @ кажись, я слегка погорячился, прованговав всем известный паттерн. ? Ну да, он самый. Но вот как раз он у вас более-менее везде в той или иной форме. Опять же, какая разница, как его реализовывать. Нравится на switch(), та на здоровье, кому-то приспичило лямбдами его зарядить и переприсваивать их mainLoop, заодно лямбдами же и предикаты переходов оформить, тазарадибога. Я вот где-то видел универсальный автомат на шаблонах, который параметризировался переходами и действиями, реализованными функторами. Формально, весь автомат метакодируется ещё при компиляции. Думал порыскать по инету и применить у себя. То-то было бы весело сравнить с коленовелосипедными. Как я и говорил, для серьёзных задач всё это самое то, но для простых процессов накладные не оправдают расходы. Зато без goto, да. Круто, чё. Ну подумаешь, фрейморк будет отжирать 90% ресурсов. |
Сообщ.
#139
,
|
|
|
Цитата Qraizer @ Ну подумаешь, фрейморк будет отжирать 90% ресурсов. Не будет. Добавлено Хотя если под какие нибудь микроконтроллеры програмить, то там да - наверно надо за памятью следить. Просто с текущими тенденциями, я не знаю - что надо взять чтоб отжирать 90% ресурсов )) |
Сообщ.
#140
,
|
|
|
Цитата Wound @ представь что 100 человек написало по 1 классу Представь что 100 человек написало по 1-й лямбде Цитата Wound @ Плюс лямбды твои, они у тебя гвоздями прибиты к переменной, объявленной в функции main -> A, а это очень херово. А в лямбду можно еще параметры передавать а видимость всего внешнего запретить. А можно вообще функциями оформить. Но мне однострочные лямбды кажутся красивее однострочных функций. |
Сообщ.
#141
,
|
|
|
Цитата Qraizer @ Я вот где-то видел универсальный автомат на шаблонах, который параметризировался переходами и действиями, реализованными функторами. Э-м… Так переходы и действия составляют 99% логики, что там в универсальной части? Цикл for? Цитата Wound @ я не знаю - что надо взять чтоб отжирать 90% ресурсов )) Думаю, Qraizer имел в виду, что из всех ресурсов, потребляемых программой, 90% будут составлять накладные расходы на фреймворк. Цитата Wound @ они будут изолированы, и представлять собой конечную сущность Не полностью: при добавлении нового процесса в твоём примере нужно будет: 1) новый процесс прописать в каком-то decision'e (откуда он должен будет запускаться) Типа – был decision3Handler -> if true then Process2.Name … – должен стать decision3Handler -> if true then Process100.Name … 2) старый процесс ( Process2.Name ) прописать куда-то в новое место И так далее, пока цепь не замкнётся. 3) зарегистрировать новый процесс и decisionHandler в ProcessContext. 4) Поправить тесты изменённых decision'ов. Итого из-за добавления одного процесса нужно править кучу классов. А как же SOLID? В наших же, с Majestio, примерах нужно править только один/два «класса» — switch-case и словарь «лямбд». И добавить тесты для новой лямбды (править тесты других decision'ов не нужно) Цитата Wound @ их можно оттестировать по отдельности например Функции (лямбды) также прекрасно тестируются по-отдельности. Цитата Wound @ Плюс лямбды твои, они у тебя гвоздями прибиты к переменной, объявленной в функции main -> A, а это очень херово. А если лямбды вынести из main в namespace challenge, будет менее херово? |
Сообщ.
#142
,
|
|
|
Эх. Надо будет хоть ваши варианты посмотреть как-нибудь, раз сам слился
Кажется, у вас тут зарождается интересный спор. |
Сообщ.
#143
,
|
||||||||||||||||||||||||||
|
Цитата D_KEY @ Кажется, у вас тут зарождается интересный спор. Он тлеет ... и я вот думаю, подбросить дровишек или нет ... И больше думаю "да" Смотрите какой вопрос Приблизительно все наши решения сводились к таблице состояний и переходов вида:
Вопрос: а что делать, если вдруг, по каким-то соображениям, нам нужно организовать так, чтобы очередной D-i возвращал не true/false, а к примеру 0..17? Блок-схему алгоритма пересматривать, таблицу переходов модернизировать? Или что-то еще ... Ваши соображения? |
Сообщ.
#144
,
|
|||||||||||||||||||||||||||||||
|
Цитата Wound @ Ну во первых мне честно лень добавлять еще 45 процессов. Это тупая копипаста получится. Схема от этого особо не изменится Детализируй текущую схему на более мелкие шаги, делов-то. Прикреплённая картинка
Полноразмерное изображение: https://snipboard.io/87seH3.jpg Добавлено Цитата Majestio @ а что делать, если вдруг, по каким-то соображениям, нам нужно организовать так, чтобы очередной D-i возвращал не true/false, а к примеру 0..17? Посмотри моё решение на Хаскелле и вопрос отпадёт сам собой. Подсказка Все процессы и проверки возвращают число: адрес следующего шага (state).
|
Сообщ.
#145
,
|
|
|
Цитата korvin @ Посмотри моё решение на Хаскелле и вопрос отпадёт сам собой. Да, объединение в одну таблицу и Process, и Decision - интересный вариант! Тогда ветвления типа switch-case можно разложить на последовательные if-then-else вообще без накладных расходов. |
Сообщ.
#146
,
|
|
|
Цитата korvin @ Не полностью: при добавлении нового процесса в твоём примере нужно будет: Фигню не неси, а? Я щас тебе таких же шагов повысасываю из пальца. Потому что, что в моих примерах, что в его примерах - сделать нужно тоже самое. В моем - добавляешь класс процесса, класс обработки решения, регистрируешь это в main. все. остальные детали идут из условия, у него это будет добавление лямбды во вторгой задаче, либо условия в switch. Цитата korvin @ Итого из-за добавления одного процесса нужно править кучу классов. А как же SOLID? Мне не нужно править кучу классов. Это ты из за того что не понимаешь что ты несешь, решил очередную чушь принести. И ничего там не нарушается. Цитата korvin @ В наших же, с Majestio, примерах нужно править только один/два «класса» — switch-case и словарь «лямбд». И добавить тесты для новой лямбды (править тесты других decision'ов не нужно) У него в решениях нет классов. Алё. И править ему придется ровно столько же, сколько и мне. Мде добавить 2 класса, и добавить их регистрацию, чтоб они вызвались. И ему две функции/лямбды. плюс добавить в его массив эти лямбды либо добавить case для первого пример. Ты мне скажи - ты может быть количество строчек считаешь? Так прямо об этом и скажи тогда, а не виляй туда сюда. Цитата korvin @ Функции (лямбды) также прекрасно тестируются по-отдельности. Я комментирую написанный уже готовый код. Иди протестируй его лямбды, не изменяя его примера. Тут притензии не к лямбдам как таковым, а к тому как они юзаются. Цитата korvin @ А если лямбды вынести из main в namespace challenge, будет менее херово? То ты уже начнешь рефакторить его код, прям сходу. |
Сообщ.
#147
,
|
|
|
Цитата Wound @ Ок |
Сообщ.
#148
,
|
|
|
Цитата Wound @ Фигню не неси, а? Вот тебе схема с новым процессом: Прикреплённая картинка
Вот ТВОЙ код с добавлением нового процесса using System; using System.Collections.Generic; using System.Linq; using Challenge.Interfaces; //! Interfaces namespace Challenge.Interfaces { public interface IDecisionHandler { string HandleDecision(bool decision); } public interface IProcess { void Execute(List<int> values); bool Decision(List<int> values); } } namespace Challenge { //! DecisionhandlerContext public class DecisionHandlerContext { private Dictionary<string, IDecisionHandler> _handlers; public DecisionHandlerContext() { _handlers = new Dictionary<string, IDecisionHandler>(); } public void RegisterHandler(string processName, IDecisionHandler decisionHandler) { if (String.IsNullOrEmpty(processName) || decisionHandler == null) throw new Exception("Argument can't be null"); _handlers.Add(processName, decisionHandler); } public string HandleDecisionAndGetNextStep(string processName, bool decision) { if (String.IsNullOrEmpty(processName)) throw new Exception("Invalid arguments"); if(_handlers.TryGetValue(processName, out var handler)) { return handler.HandleDecision(decision); } throw new Exception("Handle error"); } } public class Process1DecisionHandler : IDecisionHandler { public string HandleDecision(bool decision) { if(decision) return Process4.ProcessName; return Process3.ProcessName; } } public class Process2DecisionHandler : IDecisionHandler { public string HandleDecision(bool decision) { if (decision) return Process3.ProcessName; return Process5.ProcessName; } } public class Process3DecisionHandler : IDecisionHandler { public string HandleDecision(bool decision) { if (decision) return Process2.ProcessName; return Process1.ProcessName; } } public class Process4DecisionHandler : IDecisionHandler { public string HandleDecision(bool decision) { if (decision) return Process6.ProcessName; return Process2.ProcessName; } } public class Process5DecisionHandler : IDecisionHandler { public string HandleDecision(bool decision) { if(decision) return Process4.ProcessName; return Process3.ProcessName; } } public class Process6DecisionHandler : IDecisionHandler { public string HandleDecision(bool decision) { return String.Empty; } } //! Process1 public class Process1 : IProcess { private const int MaxSizeValues = 5; public static string ProcessName => "1"; public void Execute(List<int> values) { if (values == null || values.Count > MaxSizeValues) throw new InvalidOperationException("List of values is empty..."); ++values[0]; } public bool Decision(List<int> values) { return (values.Sum() % 2) == 0; } } //! Process2 public class Process2 : IProcess { private const int MaxSizeValues = 5; public static string ProcessName => "2"; public bool Decision(List<int> values) { return (values.Sum() % 7) > 0; } public void Execute(List<int> values) { if (values == null || values.Count > MaxSizeValues) throw new InvalidOperationException("List of values is empty..."); values[1] += values[3] + 1; } } //! Process3 public class Process3 : IProcess { private const int MaxSizeValues = 5; public static string ProcessName => "3"; public bool Decision(List<int> values) { var existOdd = values.Any(e => e % 2 != 0); return !existOdd; } public void Execute(List<int> values) { if (values == null || values.Count > MaxSizeValues) throw new InvalidOperationException("List of values is empty..."); if ( (values[0] + values[1]) % 3 > 0 ) values[2] += 7; } } //! Process4 public class Process4 : IProcess { private const int MaxSizeValues = 5; public static string ProcessName => "4"; public bool Decision(List<int> values) { return values.Sum() > 24; } public void Execute(List<int> values) { if (values == null || values.Count > MaxSizeValues) throw new InvalidOperationException("List of values is empty..."); if ((values[0] + values[2]) % 5 != 4) ++values[3]; } } //! Process4 public class Process5 : IProcess { private const int MaxSizeValues = 5; public static string ProcessName => "5"; public bool Decision(List<int> values) { return values[4] > 0; } public void Execute(List<int> values) { if (values == null || values.Count > MaxSizeValues) throw new InvalidOperationException("List of values is empty..."); ++values[4]; } } //! Process6 public class Process6 : IProcess { public static string ProcessName => "6"; public bool Decision(List<int> values) { return true; } public void Execute(List<int> values) { } } //! ProcessContext public class ProcessContext { private Dictionary<string, IProcess> _processes; private DecisionHandlerContext _handlerContext; public ProcessContext() { _processes = new Dictionary<string, IProcess>(); _handlerContext = new DecisionHandlerContext(); } public ProcessContext AddProcess(string proccesName, IProcess process, IDecisionHandler handler) { if (String.IsNullOrEmpty(proccesName) || process == null) throw new Exception("Argument can't be null"); _processes.Add(proccesName, process); _handlerContext.RegisterHandler(proccesName, handler); return this; } public bool RunProcessAndCheckDecision(string processName, List<int> values) { if(_processes.TryGetValue(processName, out var proc)) { if (proc == null) throw new Exception($"Process with name '{processName}' is null."); proc.Execute(values); return proc.Decision(values); } throw new Exception($"Process with name '{processName}' not found."); } public void RunAllProcesses(int step, string startProcessName, List<int> values) { string currentProcessName = startProcessName; while (true) { Console.WriteLine($"Step {step}\t Proc: {currentProcessName}\t {String.Join(" ", values)}"); var result = RunProcessAndCheckDecision(currentProcessName, values); currentProcessName = _handlerContext.HandleDecisionAndGetNextStep(currentProcessName, result); if (String.IsNullOrEmpty(currentProcessName)) break; ++step; } } } //! Programm public class Program { static void Main(string[] args) { try { List<int> values = new List<int> { 0, 0, 0, 0, 0 }; ProcessContext processContext = new ProcessContext(); processContext .AddProcess(Process1.ProcessName, new Process1(), new Process1DecisionHandler()) .AddProcess(Process2.ProcessName, new Process2(), new Process2DecisionHandler()) .AddProcess(Process3.ProcessName, new Process3(), new Process3DecisionHandler()) .AddProcess(Process4.ProcessName, new Process4(), new Process4DecisionHandler()) .AddProcess(Process5.ProcessName, new Process5(), new Process5DecisionHandler()) .AddProcess(Process6.ProcessName, new Process6(), new Process6DecisionHandler()); processContext.RunAllProcesses(1, Process1.ProcessName, values); Console.WriteLine("--------------------------"); List<int> savedValues = new List<int> { 3, 1, 14, 1, 0 }; processContext.RunAllProcesses(9, Process2.ProcessName, savedValues); } catch(Exception error) { Console.WriteLine(error.Message); } } } } — https://rextester.com/MWCA94654 Вот diff: https://quickdiff.net/?unique_id=C835D453-8...68-DBDE2D1B54C6 Что мы видим в данном изменении? Цитата korvin @ 1) новый процесс прописать в каком-то decision'e (откуда он должен будет запускаться) Есть? Есть: прописали Process 5 в Process2DecisionHandler. Цитата korvin @ 2) старый процесс прописать куда-то в новое место Есть? Нет: старый процесс 4 теперь вызывается из нового процесса. Если бы старый процесс нужно было бы перепривязать в другой старый процесс, то и тут было бы «Есть». Цитата korvin @ 3) зарегистрировать новый процесс и decisionHandler в ProcessContext. Есть? Есть. Цитата korvin @ 4) Поправить тесты изменённых decision'ов. Есть? Условно говоря, возможно. Тестов у тебя нет, если бы были, пришлось бы править тест Process2DecisionHandler, хотя сейчас Handler'ы выглядят тривиальными, не требующими тестирования. Итого, в таком простом изменении 2 попадания, из которых одно (пункт 1) — прямое нарушение SOILD. Как бонус, ты в своей «классной ООП-архитектуре» забыл абстрагировать ячейки памяти, в результате пришлось в каждом классе процесса править константу MaxSizeValues. Ещё одно нарушение SOILD. Кстати, почему в методе Execute размер списка проверяется, а в методе Decision — нет? Вот, например, изменения в версии на Ocaml, со switch-case-loop module type Machine = sig type data = int array val init : ?data:data -> unit -> unit val dump : unit -> data type process = unit -> unit val process_1 : process val process_2 : process val process_3 : process val process_4 : process val process_5 : process type decision = unit -> bool val decision_1 : decision val decision_2 : decision val decision_3 : decision val decision_4 : decision val decision_5 : decision end module Program (M : Machine) = struct type state = | Proc_1 | Proc_2 | Proc_3 | Proc_4 | Proc_5 | Stop let ( --> ) = ( |> ) let yes y ~no:n decision = if decision then y else n let rec run count state = log_state count state ; let loop = run (count + 1) in match state with | Proc_1 -> () --> M.process_1 --> M.decision_1 --> yes Proc_4 ~no:Proc_3 --> loop | Proc_2 -> () --> M.process_2 --> M.decision_2 --> yes Proc_3 ~no:Proc_5 --> loop | Proc_3 -> () --> M.process_3 --> M.decision_3 --> yes Proc_2 ~no:Proc_1 --> loop | Proc_4 -> () --> M.process_4 --> M.decision_4 --> yes Stop ~no:Proc_2 --> loop | Proc_5 -> () --> M.process_5 --> M.decision_5 --> yes Proc_4 ~no:Proc_3 --> loop | Stop -> () and log_state count state = let data = M.dump () in Printf.printf "Step: %2d %s %s\n" count (state_string state) (data_string data) and state_string s = match s with | Proc_1 -> "Process 1" | Proc_2 -> "Process 2" | Proc_3 -> "Process 3" | Proc_4 -> "Process 4" | Proc_5 -> "Process 5" | Stop -> "Stop " and data_string data = data --> Array.map (Printf.sprintf " %2d") --> Array.to_list --> String.concat " " end module Majestio_machine : Machine = struct let size = 5 let cell = Array.make size 0 type data = int array let init ?(data=[||]) () = let n = min size (Array.length data) in if n = 0 then Array.fill cell 0 size 0 else Array.blit data 0 cell 0 n let dump () = Array.copy cell type process = unit -> unit let process_1 () = cell.(0) <- cell.(0) + 1 let process_2 () = cell.(1) <- cell.(1) + cell.(3) + 1 let process_3 () = let sum_0_1 = cell.(0) + cell.(1) in if sum_0_1 mod 3 <> 0 then cell.(2) <- cell.(2) + 7 let process_4 () = let sum_0_2 = cell.(0) + cell.(2) in if sum_0_2 mod 5 <> 4 then cell.(3) <- cell.(3) + 1 let process_5 () = cell.(4) <- cell.(4) + 1 type decision = unit -> bool let sum = Array.fold_left (+) 0 let decision_1 () = sum cell mod 2 = 0 let decision_2 () = sum cell mod 7 > 0 let decision_3 () = let is_even x = x mod 2 = 0 in Array.for_all is_even cell let decision_4 () = sum cell > 24 let decision_5 () = cell.(4) > 0 end let main () = let module Machine = Majestio_machine in let module Program = Program (Machine) in Machine.init () ; Program.run 1 Program.Proc_1 ; print_endline "----------------------------------------------------------------" ; Machine.init ~data:[| 3; 1; 14; 1; 0 |] () ; Program.run 9 Program.Proc_2 ; print_endline "----------------------------------------------------------------" ; print_endline "done" let () = main () — https://rextester.com/WKONS22441 Вот diff: https://quickdiff.net/?unique_id=EDAE41DF-0...35-F78B02FB93DF Как видно, никаких изменений в реализациях process_2 и decision_2 в модуле Majestio_machine нет. Из дополнительного только 1) изменение размера памяти и 2) изменение самого автомата в соответствие с изменившимися связями на схеме. Таким образом, твои «независимые классы» независимы лишь иллюзорно, и твой код просто скрывает эти связи, запутывая читателя. |
Сообщ.
#149
,
|
|
|
Цитата korvin @ Вот тебе схема с новым процессом: Ааа, то есть изначально шла речь про просто добавление нового процесса, а теперь ты решил изменить условия всей задачи и других процессов, браво! Так тогда и тебе и majestio придется менять все условия. В чем вопрос, если ты изменил условия задачи? Цитата korvin @ Вот diff: https://quickdiff.net/?unique_id=C835D453-8...68-DBDE2D1B54C6 Что мы видим в данном изменении? Во первых ты там половину отсебятины добавил и переписал(зачем то, во вторых ты взял первый пример, в последнем примере там и так уже 5 процессов). Единственное что придется при добавлении нового процесса менять - это константу количества процессов. Мне лень было ее выносить куда то в отдельный базовый класс. И все. В данном случае - я прекрасно понимал что придется ее менять так же, при добавлении, но лень мне уже было ее выносить куда то. Цитата korvin @ Есть? Есть: прописали Process 5 в Process2DecisionHandler. Тогда ты так и пиши - "если я захочу изменить условия перехода для всех уже имеющихся процессов, тогда тебе придется изменять свои классы", а не "если я захочу добавить новый процесс, то тебе придется изменять уже существующие класса". Ты разницы не видишь или что? Цитата korvin @ Есть? Есть. Тебе придется в С++ коде делать ровно тоже самое, только это будет по другому называться. Я же говорю - ты из пальца высосал свои шаги, чтоб их было побольше желательно, причем все они будут так же у тебя. Цитата korvin @ Итого, в таком простом изменении 2 попадания, из которых одно (пункт 1) — прямое нарушение SOILD. Где тут нарушение S ? Цитата korvin @ Как бонус, ты в своей «классной ООП-архитектуре» забыл абстрагировать ячейки памяти, в результате пришлось в каждом классе процесса править константу MaxSizeValues. Ещё одно нарушение SOILD. Кстати, почему в методе Execute размер списка проверяется, а в методе Decision — нет? А у меня не было написать какое то идеальное решение, я уверен в моем решении еще много косяков, но это решение - прекрасно масштабируется и сопровождается. С минимальными переделками основной логики программы. Цитата korvin @ Как видно, никаких изменений в реализациях process_2 и decision_2 в модуле Majestio_machine нет. Из дополнительного только 1) изменение размера памяти и 2) изменение самого автомата в соответствие с изменившимися связями на схеме. не не не, ты в свой код внес ровно те же самые изменения что и в мой(ну за исключением константы). Ты добавил процесс, добавил какое то decision5 как ты там выразился, Ты изменил вот эту соплю или что это(предполагаю это решение процесса2): | Proc_2 -> () --> M.process_2 --> M.decision_2 --> yes Proc_3 ~no:Proc_5 --> loop Опять же изменил константу. Т.е. ты внес плюс минус те же самые изменения. в свой код. |
Сообщ.
#150
,
|
|
|
Цитата Wound @ Ааа, то есть изначально шла речь про просто добавление нового процесса, а теперь ты решил изменить условия всей задачи и других процессов, браво! Какие условия задачи? По-твоему, новый процесс куда добавляется? В вакуум? Чтобы что? Чтобы просто так висел в сторонке? Типа такого, да? Прикреплённая картинка
Цитата Wound @ Во первых ты там половину отсебятины добавил Что же там отсебятина? Цитата Wound @ Тогда ты так и пиши - "если я захочу изменить условия перехода для всех уже имеющихся процессов, тогда тебе придется изменять свои классы", а не "если я захочу добавить новый процесс, то тебе придется изменять уже существующие класса". Ты разницы не видишь или что? Что же, по-твоему, означает «добавление нового процесса»? Цитата Wound @ Где тут нарушение S ? Изменение двух классов по одной причине. Цитата Wound @ Я же говорю - ты из пальца высосал свои шаги Я тебе схему привёл, код и diff. Указанные изменения есть? Есть. Цитата Wound @ не не не, ты в свой код внес ровно те же самые изменения что и в мой(ну за исключением константы). Естественно. Ты не до конца прочитал мой пост, что ли? Цитата Wound @ Ты добавил процесс, добавил какое то decision5 как ты там выразился, Ага, добавил ровно то, что требовалось. Цитата Wound @ Ты изменил вот эту соплю или что это(предполагаю это решение процесса2): Это стрелки диаграммы, как они нарисованы на схеме, так они тут и написаны. Всё наглядно. Цитата Wound @ Опять же изменил константу. Т.е. ты внес плюс минус те же самые изменения. в свой код. Ага. Только не менял какой-то там Process2DecisionHandler, хрен пойми, где находящийся. ) Ещё раз перечитай последнее предложение: Цитата korvin @ Таким образом, твои «независимые классы» независимы лишь иллюзорно, и твой код просто скрывает эти связи, запутывая читателя. Как и «масштабируемость» твоя илюзорна. |
Сообщ.
#151
,
|
|
|
Цитата korvin @ Какие условия задачи? По-твоему, новый процесс куда добавляется? В вакуум? Чтобы что? Чтобы просто так висел в сторонке? ты поменял переходы для двух решений 2 и 4. И спрашиваешь - я ничего не менял. Я просто добавил процесс... Лол. Цитата korvin @ Что же там отсебятина? Переделал сигнатуру метода например. Цитата korvin @ Что же, по-твоему, означает «добавление нового процесса»? Это значит что ты добавляешь просто новый процесс, с новым решением. И все. Если ты начинаешь менять условия для уже существующих решений - это как не крути, называется изменение уже существующих условий и требований. Не? Ты же написал, что добавишь один процесс, и мне придется менять все свои классы. Так нет же. Если ты добавишь новый процесс, мне абсолютно ничего не придется менять. Если ты хочешь встроить свой процесс в уже существующую схему - то тебе при любых раскладах придется менять уже существующую схему, и изменения будут что у меня, что у тебя соответствующими, и никакие SOLID тут не причем. Цитата korvin @ Изменение двух классов по одной причине. По какой одной причине? Ты во первых добавляешь 2 сущности(process, decision), во вторых ты сам меняешь уже существующий алгоритм работы схемы внося в него изменения. Еще раз, где тут нарушение SOLID ? Во первых у меня вообще никак не страдает общая архитектура от этих правок. Во вторых - если не хочешь менять то что уже существует, пожалуйста - создавай еще один класс наследник, реализуй его как тебе хочется. Это ты решил изменить условия задачи, и теперь пишешь про нарушения. Чувак я тебя не понимаю, ты несешь лютейшую дичь. А если завтра ты скажешь - а я хочу вообще выкинуть процессы, и решения, и вместо этой схемы просто будет дан массив чисел, и в зависимости от того, какое число в N-ом элементе, надо вызывать соответствующую функцию. Хватит нести херню. Мне уже реально надоело обсуждать этот бред. Цитата korvin @ Я тебе схему привёл, код и diff. Указанные изменения есть? Есть. Ты поменял уже существующие условия , а не добавил новый процесс. Алё. Раз ты меняешь уже существующие условия - то ты так и пиши, если я изменю существующие условия, то тебе придется свои классы править. Только тогда это будет не аргумент, потому что тебе так же придется все твои сущности править. Ты же пишешь мне - я добавлю новый процесс и тебе придется классы править, при этом ты меняешь условия, правишь мои классы и правишь тоже самое у себя. Лол Цитата korvin @ Естественно. Ты не до конца прочитал мой пост, что ли? Ну когда ты пишешь скопом очередную херню - конечно не читаю. Суть твего запроса была в том, чтоб добавить новый процесс, а не менять условия для других. Я именно из этого и исхожу. А если ты пишешь одно, а подразумеваешь другое, так это сугубо твои проблемы. Цитата korvin @ Это стрелки диаграммы, как они нарисованы на схеме, так они тут и написаны. Всё наглядно. Ничего там не наглядно, вот эта сопля - по факту считай как мой класс Decision. Просто у тебя оно реализовано что то типа тернарного оператора с элементами лямбды, с определенный синтаксисом. Но я твой код - даже и близко не обсуждал. Мне как бы твоя функциональщина нахер не упала. Птичий язык, и разбираться в нем у меня нет ни желания, ни времени. Я глянул твой дифф, что у тебя, что у меня менять будет примерно одинаково. Единственное что синтаксис разный. Цитата korvin @ Ага. Только не менял какой-то там Process2DecisionHandler, хрен пойми, где находящийся. ) Это он для тебя хрен пойми, где находящийся, потому что я тупо взял все классы и скинул в общий блокнот, с одной единственной целью - чтоб была возможность закинуть это все в онлайн компилятор. на деле в IDE все эта кухня распологается в идеале в отдельном файле, и расположена в отдельном фильтре. И по факту не нужно ничего искать. Конкретно в этом случае - у тебя основной аргумент идет на то, что пример синтетический, и тебя толи напугали, то ли от своей тупости почему то оттолкнули классы. Но если бы вот этот каждый Decision/Process был не просто верни true/false, а что то более громоздкое, например какие то вычисления, занимающие с 10 строк, то я почему то уверен ты бы сам все это разнес бы по функциям/классам, а не лепил бы в одну строку соплей, которую ты выставляешь каким то ахеренным решением. Цитата korvin @ Ещё раз перечитай последнее предложение: А что мне перечитывать? Ты же несешь очередной бред. У меня это все не запутаннее твоего, я бы даже сказал на порядок проще, потому что глядя на твои портянки на функциональщие, - там на самом деле вообще нихера не понятно. Идеальный спагетти-код какой то. |
Сообщ.
#152
,
|
|
|
Киля, я вот тут просто вынужден согласится с korvin. Хотя мы в "Политике" с ним ацки воюем, просто моря и окияны крови ... Тут я на его стороне! Твои эти си-шарпповские "прокладки" в программировании недетерминированных алгоритмов (которые решаются автоматной логикой) вообще не упали. Да, ты можешь сказать - у меня прекрасный "программный сахар". Ну и? Не соглашусь, его over дофика получается, ну просто over! Цени дальше ...
Цитата Wound @ Если ты начинаешь менять условия для уже существующих решений - это как не крути, называется изменение уже существующих условий и требований. Не? Никто ничего не менял существенно - изменили алгоритм путем вставки процесса. Што не таг? Напомню, если забылось... Начинали со сложных много-вложенных циклов с сохранением и восстановлением вычислений и уместности прекрасного оператора GOTO. Как я и ванговал в середине обсуждения - пришли к автоматным вычислениям. Только твоя реализация слишком "сладкая", до тошноты! Хватит таблицы переходов и обработчиков (ляммб, или функций, пофик). korvin предложил оптимизацию - не разделять "процессы" и "решения". И это, ящетаю, качественная модернизация подхода! Вангую ... наберешься злости и давай спорить. Только это уже никому не интересно А вот если запилишь на шарпах обычный автомат, без ООП и прочей шляпы - тебе не будет цены!!! Вааще цена будет - очень высокая. Скрытый текст Если ты все же желаешь продвигать свою ООП-шнягу - давай строками кода схлестнемся!!! |
Сообщ.
#153
,
|
|
|
Цитата Majestio @ korvin предложил оптимизацию - не разделять "процессы" и "решения". И это, ящетаю, качественная модернизация подхода! Это даже оптимизацией-то не назвать. Ты же сам в своём первом примере объединил processN и decisionN в одну процедуру(функцию). Вот следующий шаг — вместо true/false возвращать сразу индекс процесса, который нужно запустить следующим. Можно сделать ещё один шаг: вместо индекса сразу возвращать указатель на следующую процедуру/функцию. Т.е. вместо, например, func block1() State { process1() if decision1() { return Proc4 } else { return Proc3 } } for { switch state { case Proc1: state = block1() case Proc2: ... ... case Proc5: return } } сделать так, избавившись от switch/case: type Block func() Block func block1() Block { process1() if decision1() { return block4 } else { return block3 } } ... func block5() Block { ... return nil } ... var block = block1 for block != nil { block = block() } Так немного теряется наглядность переходов от процесса к процессу, но тем не менее. Вот пример такого решения: https://youtu.be/HxaD_trXwRE?t=738 |
Сообщ.
#154
,
|
|
|
Цитата Majestio @ Вангую ... наберешься злости и давай спорить. Мне вообще пофик. Я уже встречал таких, которые втирали что монолит это клево, и что вообще писать 100 000 строк кода в функции main - в этом ничего плохого нет. И не раз с ними спорил. И сейчас в очередной раз убедился, что до сих пор такие существуют. Ну ок. Пиши как хочешь. Хоть 300 лямбд создай. Вообще пофигу. Эту задачу, что ты предложил - много ума не надо, чтоб решить с помощью If/else, если бы так стоял вопрос - я бы даже не стал участвовать в этом говно-ивенте. Как то так. Дальше уж без меня тут сритесь, мне пока не интересно дальше рассуждать. Т.к. по делу претензий не было, от слова совсем. Добавлено Цитата Majestio @ Если ты все же желаешь продвигать свою ООП-шнягу - давай строками кода схлестнемся!!! Она не моя. Она общаяя. Добавлено Цитата Majestio @ Начинали со сложных много-вложенных циклов с сохранением и восстановлением вычислений и уместности прекрасного оператора GOTO. Да, и тут ты умудрился обосраться. Сначало заливал про свой goto, а на деле - даже не понял как его применять лол. Это как знаешь, поговорка есть - На словах ты Лев Толстой, а на деле Хрен морской... Вот это как раз про тебя и твой goto Добавлено Цитата korvin @ Можно сделать ещё один шаг: вместо индекса сразу возвращать указатель на следующую процедуру/функцию. Это прям прогресс... Цитата korvin @ Так немного теряется наглядность переходов от процесса к процессу, но тем не менее. Что то мне это напомнило... А да, ты же и задизил мое решение, которое выглядит именно так. Единственное что вместо указателя на следующую процедуру/функцию - у меня возвращается название класса. Ну ниче, еще немного глядишь на ООП перепишешь, получится нечто похожее на то, что я предложил. |
Сообщ.
#155
,
|
|
|
Цитата Wound @ и что вообще писать 100 000 строк кода в функции main - в этом ничего плохого нет. А что в этом плохого, если ты реально хорошо пишешь? Можно же и на С# наговнокодить на четыре сортира. Что плохого в большом и качественном коде в функции main? |
Сообщ.
#156
,
|
|
|
Цитата Wound @ писать 100 000 строк кода в функции main - в этом ничего плохого нет. Зачем писать 100 000 строк в main? |
Сообщ.
#157
,
|
|
|
Цитата Majestio @ А что в этом плохого, если ты реально хорошо пишешь? Можно же и на С# наговнокодить на четыре сортира. Что плохого в большом и качественном коде в функции main? Гхм, ну судя по всему, с твоей точки зрения ничего плохого в этом нет... Цитата korvin @ Зачем писать 100 000 строк в main? Как выше спросил Majestio, а что в этом плохого? Адресуй этот вопрос ему, потому что у меня нет ответа на этот вопрос, и я тоже задавал адептам такого подхода - похожий вопрос. Уж не помню суть ответов, давно это было. Добавлено Цитата Majestio @ Ну и? Не соглашусь, его over дохуя получается, ну просто over! Цени дальше ... Что в твоей задаче подразумевалось под процессами? Только вот эти школьные сложить/умножить/поделить? Возьми реальный процесс, программу, который(процесс) в винде создается функцией CreateProcess например. Как ты это на свои лямбды переведешь, монолитом в функции main, расскажешь? (не, я абсолюнто уверен что это сделать не проблема, если что, не надо мне доказывать что это якобы реально) Или у тебя все задачи такого уровня, что ты привел? Добавлено Цитата Majestio @ Никто ничего не менял существенно - изменили алгоритм путем вставки процесса. Што не таг? Все так. Просто я не знаю как там korvin, лично я доказывал что тебе/ему/мне - надо сделать одно и тоже. Только выглядеть это будет по разному. Тебе условия поменять, ему соплю поправить, мне реализацию метода в классе изменить. Речь шла не об этом. Он докопался до SOLID, якобы при изменении реализации метода класса я его нарушаю, но на самом деле - в этом конкретном случае, с моей точки зрения - нет нарушения SOLID. Но откуда тебе про это знать то, если ты оперируешь goto и пишешь проги в main ? Ты просто не понял о чем мы спорили. Добавлено Цитата Majestio @ korvin предложил оптимизацию - не разделять "процессы" и "решения". Да ладно? А что он предложил, ты сам то понял? Это то что ниже твоего поста - он примером показал? Рили? Ну нихера себе оптимизация... Ты сам то понимаешь что он предлагает? Или просто поддакиваешь? |
Сообщ.
#158
,
|
|
|
Цитата Wound @ Только вот эти школьные сложить/умножить/поделить? Возьми реальный процесс, программу, который(процесс) в винде создается функцией CreateProcess например. Чтобы он в отдельном процессе вывел окошко с кнопкой Акей? Это верх программной логики А то, что существуют программы именно математических пересчетов, тебе точно не ведомо? Да, там отдельные операции чисто математические, ну что тут поделаешь. Цитата Wound @ с моей точки зрения - нет нарушения SOLID В том то и дело, что это с твоей точки зрения. Нарушаешь и не сознаешься в грехе! Не солидно получается Цитата Wound @ Это то что ниже твоего поста - он примером показал? Рили? Ну нихера себе оптимизация... Ты сам то понимаешь что он предлагает? Или просто поддакиваешь? Похоже на провок. Не понимаешь и пытаешься меня спровоцировать на объяснение. Ну ладно - расскажу, если тебе лениво самому разобраться. В последних обсуждениях я задал вопросы, что делать если в алгоритме появляются безусловные переходы между процессами, или множественные (а-ля) switch-case "решения". Так вот, в моем случае, когда процессы и решения разделены - это можно решать с помощью "процессов-заглушек", либо "решений-заглушек", т.к. у меня идет попарный вызов -"процесс-решение". В его же предложении есть хорошее зерно - объединить все вызовы в единую таблицу. Где все вызовы делаются вне зависимости от типа вызываемой функции или лямбды. И это сразу же избавляет от необходимости введения этих самых "заглушек". Код чище и оптимальнее. Кстати, когда будешь использовать GOTO из главной функции main прямо в тело другой функции - не забудь поправить стек! Иначе по выходу из функции полетит память, и не сможешь вывести кнопку Акей! Хотя ... ты же на сишарпе кодишь - вам там вообще нельзя с железными регистрами проца и памятью работать напрямую. Или можно? Кстати, стадию отрицания оператора GOTO можно быстрее и успешнее пережить - посетив всего пару курсов экстремального быдлокодинга на бейсеке |
Сообщ.
#159
,
|
|
|
Цитата Majestio @ Чтобы он в отдельном процессе вывел окошко с кнопкой Акей? Это верх программной логики А то, что существуют программы именно математических пересчетов, тебе точно не ведомо? Да, там отдельные операции чисто математические, ну что тут поделаешь. Нет. Я тебе пытаюсь донести что в твоем случае - сам процесс какая то высосаная из пальца однострочная фиговина. Представь что процесс - это целая программа. Цитата Majestio @ Похоже на провок. Не понимаешь и пытаешься меня спровоцировать на объяснение. Ну ладно - расскажу, если тебе лениво самому разобраться. В последних обсуждениях я задал вопросы, что делать если в алгоритме появляются безусловные переходы между процессами, или множественные (а-ля) switch-case "решения". Так вот, в моем случае, когда процессы и решения разделены - это можно решать с помощью "процессов-заглушек", либо "решений-заглушек", т.к. у меня идет попарный вызов -"процесс-решение". В его же предложении есть хорошее зерно - объединить все вызовы в единую таблицу. Где все вызовы делаются вне зависимости от типа вызываемой функции или лямбды. И это сразу же избавляет от необходимости введения этих самых "заглушек". Код чище и оптимальнее. Если ты внимательно посмотришь на его пример, и в мой код Handler'ов, и там где они вызываются, то возможно увидишь схожесть... |
Сообщ.
#160
,
|
|
|
Цитата Wound @ Нет. Я тебе пытаюсь донести что в твоем случае - сам процесс какая то высосаная из пальца однострочная фиговина. Представь что процесс - это целая программа. Вот ты и попался, который кусался! Запомни, с твоим таким подходом - нельзя полноценно остановить, запомнить состояние. А потом возобновить. Любые процессы длинною в жысть нужно дробить на мелкие. Вот тогда и только тогда у тебя появится возможность приостановления и сохранения состояния оперативно. Ну и возобновления. Андестенд? Цитата Wound @ Если ты внимательно посмотришь на его пример, и в мой код Handler'ов, и там где они вызываются, то возможно увидишь схожесть... Просто в твоем коде овер дохера букв, а у него нет. Ну х3, но если оно так - ну чо, респект ему и тебе. А мне нет. |
Сообщ.
#161
,
|
|
|
Цитата Majestio @ Ну... зачем так сурово-то. Гораздо интереснее скриптовать на CMD. Самый мощный мой скрипт размером 64К был. Там сборка в трёх конфигурациях, две из которых инструментирующие, модульных тестов для 47 различных функциональностей и их прогон с генерированием отчётов под 8-ю разными (не x86 даже близко) процессорами на стендах с использованием 8-и же разных сред разработки. Параметры сборки в частности определялись ещё и парсингом текста тестов, исполнительное тестовое окружение могло быть тоже 3-х разных версий и до кучи есть несколько исключений и правил, прописанных опять же в стороннем файле, который тоже нужно было парсить. И всё это в параллель, но одновременно не более чем количество ядер у процессора + количество подключённых стендов. Как я семафор для ограничения параллельных сборок писал, это отдельный экзерсис. Вот где goto во всей красе, без него даже циклы нормально написать зачастую невозможно. Кстати, стадию отрицания оператора GOTO можно быстрее и успешнее пережить - посетив всего пару курсов экстремального быдлокодинга на бейсеке Добавлено Вот для примера определение номера партиции (некоторые тесты из-за размера приходилось бить на части) теста в его ID. rem /**************************************************\ rem | Получить номер парта из ID теста, | rem | если он есть | rem \**************************************************/ :getPartNumber setlocal set testName=%1 rem проверить наличие деления теста на парты if "%testName%" EQU "%testName:_PART_=%" (endlocal & set result= & exit /b) set res= rem найти номер парта в ID теста :scanPart rem разделители частей _ и . for /f "tokens=1,* delims=_." %%x in ("%testName%") do ( rem отсеять предшествующие части ID if "%%x" == "PART" ( rem номер парта будет следующей частью for /f "tokens=1,2 delims=_." %%y in ("%testName%") do set res=%%z ) else ( rem удалить начало ID из него set testName=%%y goto scanPart ) ) rem номер найден endlocal & set result=_PART_%res% exit /b |
Сообщ.
#162
,
|
|
|
Цитата Qraizer @ Вот где goto во всей красе, без него даже циклы нормально написать зачастую невозможно. По красоте. Да здравствует прекрасный оператор GOTO!!! |
Сообщ.
#163
,
|
|
|
Кстати ... для оформления комментов в CMD-скриптах рекомендую использовать вместо REM пару двоеточий, смотрится красивше.
Вот пример: :: :: ┌───────────────────────────────────────────────────────────────────────────┐ :: │ Получить номер парта из ID теста, │ :: │ если он есть │░░ :: └───────────────────────────────────────────────────────────────────────────┘░░ :: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ :getPartNumber setlocal set testName=%1 rem проверить наличие деления теста на парты if "%testName%" EQU "%testName:_PART_=%" (endlocal & set result= & exit /b) set res= rem найти номер парта в ID теста :scanPart rem разделители частей _ и . for /f "tokens=1,* delims=_." %%x in ("%testName%") do ( rem отсеять предшествующие части ID if "%%x" == "PART" ( rem номер парта будет следующей частью for /f "tokens=1,2 delims=_." %%y in ("%testName%") do set res=%%z ) else ( rem удалить начало ID из него set testName=%%y goto scanPart ) ) rem номер найден endlocal & set result=_PART_%res% exit /b |
Сообщ.
#164
,
|
|
|
Цитата Majestio @ Вот ты и попался, который кусался! Запомни, с твоим таким подходом - нельзя полноценно остановить, запомнить состояние. А потом возобновить. Любые процессы длинною в жысть нужно дробить на мелкие. Вот тогда и только тогда у тебя появится возможность приостановления и сохранения состояния оперативно. Ну и возобновления. Андестенд? Если честно - ничего не понял Цитата Majestio @ Просто в твоем коде овер дохера букв, а у него нет. Ну х3, но если оно так - ну чо, респект ему и тебе. А мне нет. А ты не на буквы смотри, а на контекст(суть программы, а не строчки считай). |
Сообщ.
#165
,
|
|
|
Цитата Qraizer @ Вот для примера определение номера партиции (некоторые тесты из-за размера приходилось бить на части) теста в его ID. Тут, на сколько я понимаю, с помощью оператора goto - сделан искуственный цикл(попадаем в ветку else - и переходим к началу for, с обновленным начальным массивом). С тем же успехом можно написать вложенный цикл, либо проходить массив не итерированием, а по индексам, и не юзать goto. Если это единственное оправданное применение goto - то как то не убедительно выглядит. Давайте приводите что то более существенное. |
Сообщ.
#166
,
|
|
|
Цитата Wound @ Оправданием служит то, что командный язык CMD ...как бы так помягче... эзотерический. Если это единственное оправданное применение goto - то как то не убедительно выглядит. |