Может в моддинге я новичок, но в программирование далеко не нуб.
Я ходил в очень строгую школу для кодеров
Это очень здорово, что ты пришел в моддинг имея начальные знания и навыки программирования!
Лишняя библа, надстройка кода над обычной функцией и есть антоним оптимизации.
Чтобы случайно не превратить дискуссию в спор о словах и их определениях, давай для начала определим, о чем мы спорим.
Насколько я понимаю, речь идет о быстродействии программы. Т.е. мы считаем программу
оптимальной, если она решает задачу за
минимальное возможное время. Стоит отметить, что время выполнения программы - объективная метрика. Если два программиста будут стоять рядом и смотреть на одну и ту же работающую программу, то она будет работать одинаково быстро с точки зрения обоих программистов.
Все верно?
Надеюсь что так, поэтому продолжаю дальше.
Теперь определимся с костылями.
В инете можно найти много разных определений. Обычно имеется ввиду код, который выполняет функцию "подпорки", чтобы эта часть программы работала. Часто это попытка заставить программу работать любой ценой, игнорируя правила хорошего кода, не разбираясь в специфике ошибки и т.д. Стоит отметить, что костыльность кода зависит от конкретного наблюдателя: два программиста могут стоять рядом, смотреть на код одной и той же программы и оценивать его костыльность по разному.
Теперь вернемся к вопросу ортогональности этих двух вещей. Когда я говорю, что две вещи ортогональны, я подразумеваю, что эти две вещи могут сочетаться в разных взаимо-независимых пропорциях.
- Код, который быстро работает и является костылем.
- Код, который медленно работает и является костылем.
- Код, который быстро работает и не является костылем.
- Код, который медленно работает и не является костылем.
Почему все четыре кейса(а также их более тонкие градации) могут существовать?
Это может быть очевидно, исходя из того, что оптимальность по времени выполнения - объективная характеристика кода, а костыльность - субъективная.
Также можно найти живые примеры на каждый кейс. Так как ты в основном сомневаешься в первом и в последнем кейсах, то я приведу примеры для них.
Допустим, разработчик делает рендер графического интерфейса на чистом opengl. Разработчик правильно выровнял один из элементов гуи где-то в середине процесса. Ближе к концу работы он замечает, что этот элемент съезжает на 10 пикселей правее. Разработчик не видит очевидных причин этому явлению. Т.к. в остальном все работает нормально, то он просто добавляет этому элементу дополнительное смещение при помощи glTranslate(10,0,0). Опять тестирует и все работает хорошо. Эта дополнительная инструкция практически не замедлила рендер, в коде и без того куча операцией с матрицами трансформации. При этом такое решение является костылем, т.к. разработчик не выяснял о глубинных причинах бага.
Допустим, имеется список дробных чисел и стоит микро-задача применять ко всех элементам списка какие-то функции преобразования, типо, умножить на два. Формализуем ее как метод
void transformList(Function<Double, Double> mapper)
При этом другой части программы может единовременно быть нужен только сегмент списка.
Как мы можем реализовать функцию transformList?
Самое простое, что можно сделать, это обойти список и обновить значения
private List<Double> list = new ArrayList<>();
public void transformList(Function<Double, Double> mapper){
for(int i=0;i<list.size();i++)
list.set(i, mapper.apply(list.get(i)));
}
Костыль? Вроде бы нет, вполне очевидный код, который делает то, что ожидается.
Он быстрый? Временная сложность алгоритма
O(n)
: если у нас список длиной n, то нужно произвести n операций.
Можно ли сделать быстрее? Из специфики программы известно, что единовременно потребляется только некоторая часть списка. Если список длинный, а окно потребления маленькое, то имеет смысл применять преобразование только к элементам, которые нужны в текущий момент. Это позволит снизить временную сложность до O(k), где k - размер окна потребления.
Получается примерно так(детали, связанные с высвобождением более не используемых списков и некоторые другие вещи опущены для простоты):
private List<Double> list = new ArrayList<>();
public void transformList(Function<Double, Double> mapper){
list = new LazyTransformList(list, mapper);
}
public static class LazyTransformList<A> extends List<A> {
private List<A> original;
private Function<A,A> mapper;
private List<A> transformed;
public LazyTransformList(List<A> original, Function<A,A> mapper) { ... }
public A get(int index){
A exists = transformed.get(index);
if(exists!=null)
return exists;
else{
A newValue = mapper.apply(original.get(index));
transformed.set(index, newValue);
return newValue;
}
}
public void set(int index, A newValue){
transformed.set(index, newValue);
}
}
Таким образом, первый вариант реализации не самый быстрый
не нужная библа = -1 бал, два таких косяка и уже еле набираешь проходной бал,
Вероятно, это предназначено для того, чтобы студенты учились сами реализовывать логику, вместо того, чтобы вызвать одну готовую функцию из библиотеки.
Библиотеки могут быть или не быть оптимально (по разным критериям) написаны