LINUX.ORG.RU

Чем плохо много return?

 ,


0

3

В линтерах во многих языках программирования есть такая проверка: https://docs.astral.sh/ruff/rules/too-many-return-statements/

По ссылке приведён тривиальный пример, где достаточно сопоставления по ключу.

В общем случае код выглядит примерно как:

def f(x):
  if cond1(x):
    return res1
  y = process1(x);
  if cond2(y):
    return res2
  if cond3(y):
    return res3
  z = process2(x, y)
  if cond4(x,y,z):
    return res4
  if cond5(x,y,z):
    return res5
  ...  

после убирания return получается что-то вроде

def f(x):
  done = False 
  if cond1(x):
    res = res1
    done = True
  if not done:
    y = process1(x);
  if not done and cond2(y):
    res = res2
    done = True
  if not done and cond3(y):
    res = res3
    done = True
  if not done:
    z = process2(x, y)
  if not done and cond4(x,y,z):
    res = res4
    done = True
  if not done and cond5(x,y,z):
    res = res5
    done = True
  ...
  return res  

Второй вариант действительно легче читается или я неправильно преобразовал первый во второй?

UPD: С учётом наличия elif

def f(x):
  done = False 
  if cond1(x):
    res = res1
    done = True
  if not done:
    y = process1(x);
    if cond2(y):
      res = res2
      done = True
    elif cond3(y):
      res = res3
      done = True
  if not done:
    z = process2(x, y)
    if cond4(x,y,z):
      res = res4
      done = True
    elif cond5(x,y,z):
      res = res5
      done = True
  ...
  return res  

Вот это читается легче, чем первый вариант?

★★★★★

Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от qulinxao3

хотя там есть в ga68 ключик -fa68-dump-ast, надо в районе него покопать… как плагин к gcc на самом *.a68 с алголопаскалемодулями написать, чтобы этот AST недолиспом или недорефалом написанным на алголе преобразовывать…

anonymous
()
Ответ на: комментарий от peregrine

краткость — сестра таланта

В айтишечке самое дорогое это менять код. Поэтому код нужно структурировать так, чтобы его не нужно было менять. Если ты уверен что твой алгоритм будет один раз будет написан и запакован в чёрный ящик – да, можешь сделать однострочник. Если нет – лучше раздели на движущиеся части, чтобы каждую можно было целиком выкинуть и написать с нуля (а не менять постоянно, делая неподдерживаемой лапшой). Чтоб «трение» о другие компоненты свести к минимуму при изменении.

P.S. пример выше мне сгенерировала гопота, и скорее всего он дичь. Но принцип примерно такой

oxo
()
Ответ на: комментарий от oxo

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

Так строка или синтаксическая конструкция программы и есть такая движущаяся часть. Не понимаю стремления сделать из программы интерпретатор псевдокода в лучшем случае на XML, а в худшем на последовательности адресов функций с идентичной сигнатурой.

monk ★★★★★
() автор топика
Ответ на: комментарий от oxo

Inversion of Control.

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

monk ★★★★★
() автор топика
Ответ на: комментарий от oxo

Инверсия управления должна быть для уменьшения связности. Сама по себе низкая связность неплохо (хотя и имеет цену в разработке), но реально она нужна для двух вещей: масштабирования и отказоустойчивости, при том второе не всегда верно, иногда лучше чтоб программа полностью грохнулась, чем обработала данные частично (разбиение на независимые модули с точки зрения разработки командой, возможность делать микросервисы и располагать их на разных машинах, что облегчает перезапуск системы в случае падения части сервисов и какую-то частичную работоспособность, по типу главная страничка работает, а вот вики нет). Крайне высокая связность - плохо (всегда плохо когда состояния 2 модулей связаны более чем в одном месте или связаны сложным образом через несколько других модулей, но это и дураку понятно). Но это вовсе не значит что надо упарываться и изобретать фреймворк и метапрограммирование на каждый чих. Нет, не надо. А вот у самой инверсии управления тоже есть свои недостатки: логика программы разбросана по куче мест (опять если цель делать бекдоры то это очень хорошо, вон в xz китайцы догадались аж в систему сборки логику бекдора всунуть, благо можно), поток управления неявный и использует общее состояние обработчиков событий, а это может быть плохо с точки зрения производительности, особенно в странных случаях, когда происходит что-то необычное.

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)
Ответ на: комментарий от oxo

В айтишечке самое дорогое это менять код.

Кстати, тоже недоказанное утверждение.

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

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

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

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

недоказанное утверждение

В качестве примера можно взять хоть скрипты в init.d,

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

oxo
()
Последнее исправление: oxo (всего исправлений: 1)
Ответ на: комментарий от peregrine

но реально она нужна для двух вещей: масштабирования и отказоустойчивости

этот как раз глубоко вторично

в первую очередь она нужна для уменьшения радиуса разлёта осколков при изменениях

во вторую – священный грааль всего программирования – для композиции

oxo
()
Ответ на: комментарий от oxo

Плохо когда в какой-то деятельности возникает священная корова. Очень плохо. Это только для индусов хорошо. Ну и кода индусского.

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)

А мне кажется, что много reutrn это намного лучше, чем много elif. Например в том же Хаскеле - это норма. По сути определяется значение функции для каждого кейсы прямо в момент return.

anonymous
()
Ответ на: комментарий от oxo

Минимальное трение.

А внутри скрипта максимальное. И новый скрипт создаётся путём копирования похожего, а не параметрами к универсальной запускалке через Inversion of Control.

monk ★★★★★
() автор топика
Ответ на: комментарий от oxo

в первую очередь она нужна для уменьшения радиуса разлёта осколков при изменениях

Для этого достаточно модульности и фиксации API между модулями.

во вторую – священный грааль всего программирования – для композиции

Вот именно. Постулат, принимаемый на веру.

monk ★★★★★
() автор топика
Ответ на: комментарий от anonymous

А мне кажется, что много reutrn это намного лучше, чем много elif. Например в том же Хаскеле - это норма.

В Хаскеле в IO вообще нет досрочного выхода из монады. Или как напишешь аналог

def f(x):
  if cond1(x):
    return res1
  y = process1(x);
  if cond2(y):
    return res2
  else
    return res3

где

cond1 :: Int -> IO Bool
process1 :: Int -> IO Int
cond2 :: Int -> IO Bool

?

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

И новый скрипт создаётся путём копирования похожего

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

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

В айтишечке самое дорогое это менять код.

oxo
()
Ответ на: комментарий от oxo

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

Так тогда вообще никогда не надо изменять существующий работающий код (кроме исправления ошибок). Если требования изменились, создаётся новый код на основе старого.

И тогда вся эта морока со слабым связыванием смысла не имеет, так как код никогда не меняется и задачи «изменить код модуля так, чтобы вся остальная программа не менялась» не существует.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Так тогда вообще никогда не надо изменять существующий работающий код (кроме исправления ошибок). Если требования изменились, создаётся новый код на основе старого.

Да, именно так. Собственно про это open closed principle.

И тогда вся эта морока со слабым связыванием смысла не имеет,

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

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

Но кажется что дешевле всего когда достаточно написать новый компонент и скомозировать его с существующими. Про это уже SRP – каждый компонент под одно требование + Pure Fabrication

oxo
()
Последнее исправление: oxo (всего исправлений: 2)
Ответ на: комментарий от oxo

Имеет, если ты не хочешь переписывать проект целиком.

Так если это новый проект, его всё равно переписывать целиком. А если старый, то мы только что договорились, что его не надо переписывать никогда.

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

Моё личное мнение: новый компонент в существующую систему можно написать только тогда, когда задачей является именно реализация компонента системы (например, «реализовать … для ОС Linux с графическим интерфейсом на Gtk»). Во всех остальных случаях попытка использовать существующую систему компонентов почти всегда выливается в набор костылей и велосипедов на сопряжении с ней.

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

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Так если это новый проект, его всё равно переписывать целиком. А если старый, то мы только что договорились, что его не надо переписывать никогда.

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

Но люди хотят экономить

Не совсем так. Скорее, если затраты на написание софта больше чем он принесёт дохода – его просто не будут писать.

oxo
()
Последнее исправление: oxo (всего исправлений: 1)
Ответ на: комментарий от monk

новый компонент в существующую систему можно написать только тогда, когда задачей является именно реализация компонента системы

Допустим, у нас есть условный grep, и мы хотим в него добавить поддержку новой библиотеки парсинга по регулярным выражениям. Если grep написан так, что подобное изменение приведёт к изменению 95% кода grep – к гадалке не ходи что мы сломаем какую-то старую стабильную функциональность, а самое изменение будет долгим и тяжелым. Если grep написан так, что в diff у нас только добавился новый код в новые файлы, а изменений там код наплакал – скорее всего у нас баги только в новой логике, добавление прошло дёшево, а ещё нам так же дёшево будет эту функциональность удалить если практика покажет что она там не нужна.

oxo
()
Последнее исправление: oxo (всего исправлений: 1)
Ответ на: комментарий от oxo
  1. В этом деле главное — вовремя остановиться. Потому что «добавить поддержку новой библиотеки парсинга по регулярным выражениям» имеет смысл, а вот добавлять в grep полнотекстовый поиск — уже нет. И точка расширения, позволяющая оный поиск добавить, только запутает архитектуру, усложнит код и будет тормозить разработку.

  2. Вовремя останавливаться в этом нашем ИТ не умеют.

ugoday ★★★★★
()
Ответ на: комментарий от oxo

Допустим, у нас есть условный grep, и мы хотим в него добавить поддержку новой библиотеки парсинга по регулярным выражениям.

Если она полностью совместима со старой по пользовательскому синтаксису, но быстрее, то надо переписывать grep на новую.

Если она совсем новая, надо писать новый grep-ng с другим синтаксисом и не пытаться поддерживать два сразу.

Можно вспомнить переход от Netscape 6 к Mozilla. В итоге пришлось всё выкинуть и переписать.

Или можно вспомнить Windows. Который из-за желания совместимости до сих пор хранит дополнительный 8-символьные имена файлов и поддерживает одновременно две кодировки (для файловой системы одна, для текста другая).

Единственный случай, когда это можно сделать, если grep изначально написан для использования внешней библиотеки парсинга по какому-то протоколу и новая библиотека парсинга этот протокол поддерживает. Но для этого при проектировании надо чётко определять, какие части программы считаем внешними, а какие частью программы. Потому что если не поставить рамки, то тот grep превратится в программу, которая читает данные из источника А, обрабатывает их библиотекой Б и передают результат в результат В. И в таком виде она совершенно не отличается от cat, sed и awk кроме подключаемой библиотеки. И реальной программой становится та самая библиотека.

monk ★★★★★
() автор топика

Вот это читается легче

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

dmitry237 ★★★★★
()
Ответ на: комментарий от monk

надо

С точки зрения практики нужно чтобы grep сначала поддерживал старую и новую версию одновременно и можно было переключатся между ними. Когда станет понятно что новая версия норм, клиенты собрали баги, и дали положительный фидбек – задеприкейтить старую и сделать новую по дефолту. Если будет понятно что не норм – убрать новую. А потом ещё 20 лет поддерживать старую, потому что есть 2 кейса когда почему-то старая очень нужна, и за неё платят деньги.

oxo
()
Ответ на: комментарий от oxo

С точки зрения практики нужно чтобы grep сначала поддерживал старую и новую версию одновременно

Зачем? Чем это лучше двух независимых программ?

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

В случае новой отдельной программы это работает автоматически.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Зачем? Чем это лучше двух независимых программ?

Потому что это интерфейс. Если мы говорим про пример grep – потому что grep используется в куче скриптов. Если речь например про публичное API – потому что его уже много кто использует, и апать его версию нужно только при невозможности сохранить совместимость.

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

В случае новой отдельной программы это работает автоматически.

Только сначала тебе нужно совершить работу по переходу.

oxo
()
Последнее исправление: oxo (всего исправлений: 2)
Ответ на: комментарий от oxo

Потому что это интерфейс.

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

И даже если полностью совместима. Вот есть интерфейс sh. Но это же не помешало сделать bsh, bash, dash, ksh, zsh, … А не пихать всё в один sh.

Собственно, это всё о том же: уменьшение трения при изменениях до минимума, уменьшение разлёта осколков => удешевление

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

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

monk ★★★★★
() автор топика
Ответ на: комментарий от oxo

Только сначала тебе нужно совершить работу по переходу.

Её в любом случае нужно совершить. То, что это за разработчика делает сопровождающий дистрибутива (maintainer), тоже не самый лучший вариант.

monk ★★★★★
() автор топика
Ответ на: комментарий от oxo

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

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

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

Kogrom
()
Ответ на: комментарий от oxo

Inversion of Control

Чувствуется влияние Мартина Фаулера и прочих гуру, типа Кента Бека и Роберта Мартина. Все эти ребята являются порождением «тучных годов», когда ПО в первую очередь разрабатывалось для банков, торговли и тому подобных сфер. Денег много, программистов много, требования к быстродействию и потреблению оперативной памяти - минимальны. Если вы в таких же условиях, то разумно следовать за этими гуру. Если нет, то подражание им - это «культ карго», которое только вредит разработке.

Kogrom
()
Ответ на: комментарий от Kogrom

We Programmers Мартина делает их(этих шарло-гуру) более обьёмнным

ваще когда есть килобайты свободной памяти то решения больше похожи на решения когда в наличии ещё больше памяти чем на решения когда памяти считанные регистры

будь у публики полное понимание массивов(что индексируемых целыми)(что асоциативных) то и код был бы более взаимноболеечитаемым

иначе получается что под читаемостью кода оказывается общие пересечение скиллов прогеров - как результат тотальное неграмотное программирование отсюда и культ шаблонов(патернов) как более экономически выгодное на краткосроке чем качественно подготовленные кодеры

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

qulinxao3 ★☆
()
Ответ на: комментарий от qulinxao3

качественно подготовленные кодеры

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

Кстати, байки из жизни: самое большое количество самого херового кода я видел на лиспе (clojure) просто потому что это мой основной язык последние 12 лет. Просто потому, что лисповики зачастую убеждены что solid, grasp, ddd и всё такое прочее это для «джава-даунов», а на кложе то, с такими то волшебными структурами данных можно наконец-то нормально писать! В результате получается код уровня лапшеphp из хоумпаг 90ых. А вот PHPшники писать хороший код научились.

oxo
()
Ответ на: комментарий от oxo

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

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

Модульное проектирование было не для того, чтобы плохой код можно было заменить, а для того, чтобы можно было проектировать структуру верхнего уровня опираясь на готовые модули нижнего. Как у строителей: розетки, крепления, балки, затем панели, затем дом. Но у них никто не проектирует дом, чтобы в нём можно было в любой момент заменить все несущие балки. Даже если их заменять на другие не в готовом доме, а в проекте, это всё равно подразумевает пересчёт всего, что вокруг.

В результате получается код уровня лапшеphp из хоумпаг 90ых. А вот PHPшники писать хороший код научились.

Критерий «хороший» многозначный. Если «хороший» = «соответствующий solid, grasp, ddd и всё такое прочее», то всё так и есть. Если же «хороший» = «компактный, быстрый, корректный», то иногда лапшеphp в этом смысле намного лучше нынешних многослойных конструкций, которые падают в совершенно неожиданном месте.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

никто не проектирует дом, чтобы в нём можно было в любой момент заменить все несущие балки.

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

проектировать структуру верхнего уровня опираясь на готовые модули нижнего

ммм, протекающие наверх абстракции

oxo
()
Ответ на: комментарий от oxo

Хороший это который за свой жизненный цикл принесёт денег побольше, а затратит поменьше.

По этому критерию тоже лапшеphp выигрывает почти всегда. Потому что за него платить приходится в разы меньше, а доход такой же. Доля проектов, которые внезапно приходится значительно переписывать, но с сохранением старой кодовой базы, очень мала: или изменения косметические и легко вносятся даже в лапшеphp или изменения типа «мы переходим с Joomla на Wordpress», тогда неважно в каком стиле был старый проект, его весь переписывать.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Доля проектов, которые внезапно приходится значительно переписывать, но с сохранением старой кодовой базы, очень мала

Видимо мне не повезло, всё время попадаю в этот маленький процент.

oxo
()
Ответ на: комментарий от oxo

Наверное это дорого просто в случае дома и кейсы редко возникают.

Для программирования тоже дорого. Но «тут так принято».

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

Очень ограниченно. Обычно только на идентичное из-за износа. Поставь коробку передач от мерседеса на вольво.

ммм, протекающие наверх абстракции

Именно. В нормальной программе никто не пишет на абстракциях. Данные хранятся не в произвольной коллекции, а в конкретных линейных списках, массивах, деревьях. С соответствующей ценой операции.

Попытка взять почти любой алгоритм лиспа и в лоб перенести его на C++ std::vector уменьшит скорость работы на порядки.

Абстракция это миф. Нужна надёжная реализация модулей, от которых зависит твоя программа (даже Linux собирается не абстрактным компилятором Си, а только gcc).

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

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

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

Например, на лоре раньше был только LORCODE, а стал ещё и Markdown. Реакций раньше небыло, теперь есть. Не создавать же новый лор из-за этого? Можно представить что реакции допустим отпугнули бы значительную часть аудитории и тогда бы их макском выпилил бы обратно их. Так же раньше лору достаточно было вывозить один запрос в секунду, а сейчас допустим 100.

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

oxo
()
Последнее исправление: oxo (всего исправлений: 1)
Ответ на: комментарий от oxo

Реакций раньше небыло, теперь есть. Не создавать же новый лор из-за этого?

По-хорошему, создавать.

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

А под это почти всегда создавать, если изначально не закладывалось такое требование.

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

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

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

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

monk ★★★★★
() автор топика