Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.15.141.184] |
|
Страницы: (12) « Первая ... 8 9 [10] 11 12 все ( Перейти к последнему сообщению ) |
Сообщ.
#136
,
|
|
|
Цитата Wound @ Именно, оно уже выглядит пугающим. Когда там будет 100 лямбд - то уже будет на что посмотреть. Не, ну можно 100 классов объявить вместо лямбд, как два пальца об асфальт |
Сообщ.
#137
,
|
|
|
Цитата Majestio @ Не, ну можно 100 классов объявить вместо лямбд, как два пальца об асфальт Фишка в том, что даже если будут 100 классов - они будут изолированы, и представлять собой конечную сущность, их можно оттестировать по отдельности например, представь что 100 человек написало по 1 классу. А с лямбдами сложнее выходит. Плюс лямбды твои, они у тебя гвоздями прибиты к переменной, объявленной в функции main -> A, а это очень херово. Добавлено Короче как по мне - даже первый твой вариант без GOTO, выигрывает по всем параметрам второй варинт с GOTO. И по читабельности кода, и по сопровождению. Потому что первый вариант можно и оттестировать, и расшрить без особых проблем. Второй вариант(который на лямбдах) - как по мне трешак, который придется полностью переписывать, если чуть условия усложнить. |
Сообщ.
#138
,
|
|
|
Цитата korvin @ Я большей частью имел в виду хранение состояния через сериализацию. Как оно у кого будет реализовано, как бы без разницы. Я вот думал забабахать boost:serialisation, шоб вам тут мёдом не. Ну да, он самый. Но вот как раз он у вас более-менее везде в той или иной форме. Опять же, какая разница, как его реализовывать. Нравится на 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, будет менее херово? То ты уже начнешь рефакторить его код, прям сходу. |
Сообщ.
#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 @ Таким образом, твои «независимые классы» независимы лишь иллюзорно, и твой код просто скрывает эти связи, запутывая читателя. Как и «масштабируемость» твоя илюзорна. |