Pull to refresh
@yurisv3read⁠-⁠only

User

Send message
«Шо, опять?!» (с) известный мультфильм.

(присаживается поудобнее в кресло-качалку, накрывается клетчатым пледом, наливает в кружку что-то горячее с молоком, отхлебывает)

А ведь я застал-с те времена, когда знание алгоритмов всяческих там сортировок, деревьев и прочих списков было обязательным, да-с…

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

Вся работа начиналась вот примерно с таких креативов
struct foo_nlist {
    struct foo_nlist* next;
    ...
};

struct foo_nlist* foo_lookup(struct foo_nlist* root, ...);
struct foo_nlist* foo_insert(struct foo_nlist* root, ...);
struct foo_nlist* foo_remove(struct foo_nlist* root, struct foo_nlist* elem);

в тяжелых случаях вытаскивался препроцессор
#pragma pack(1)

struct nlist_header {
    struct nlist_header* next;
};

#define DECLARE_LIST_ENTRY(foo_nlist_name, foo_type) \
    struct foo_nlist_name { \
        struct nlist_header header; \
        foo_type value; \
    }

struct foo {
    ...
};

struct bar {
    ...
};

#define TO_VALUE(header, foo_type) \
    ((foo_type*)((char*)(header) + sizeof(struct nlist_header)))
#define TO_LIST(value) \
    ((struct list_header*)((char*)(value) - sizeof(struct nlist_header)))
 
DECLARE_LIST_ENTRY(foo_nlist, struct foo) foo_root;
DECLARE_LIST_ENTRY(bar_nlist, struct bar) bar_root;

struct nlist_header* nlist_lookup(struct nlist_header* root, ...);
struct nlist_header* nlist_insert(struct nlist_header* root, ...);
struct nlist_header* nlist_remove(struct nlist_header* root, struct nlist_header* elem);

На этом полет креатива не останавливался — появлялись mutex для параллельной работы, списки менялись на деревья и прочая, а для работы со структурами данных изобретался паттерн visitor в стиле «лямбд пока не завезли»:

#define FOR_EACH_NLIST(foo_type, item, nlist) { \
    for(struct nlist_header* hdr = (nlist); hdr; hdr = hdr->next) { \
        foo_type* item = TO_VALUE(hdr, foo_type);
#define FOR_EACH_END() \
        }\
    }
...
    FOR_EACH_NLIST(struct foo, foo_item, foo_root) 
        if (foo_item-> ..) {
            FOR_EACH_NLIST(struct bar, bar_item, bar_root) 
                ...
            FOR_EACH_END()
        }
    FOR_EACH_END()


Это сейчас за такое я джунов луплю по рукам линейкой не одобряю, а тогда… тогда даже строки были квадратно-колесными, и в приличном проекте их было полдюжины разных. Роднило их только одно — все они умели в char*, а наиболее грамотные — еще и в const char*.

Это сейчас за попытку изобрести свой аналог std::string ваш лид спросит — в своем ли вы уме, а 25 лет назад он бы поинтересовался, как это удалось сделать copy-on-write и при этом работающую форму
str[n] = 'a';
и как при этом избежать O(n^2) в случае классики
result = a + "\\" + b +"\\" + c + "." + d; 


Следом обычно шел кастомный аллокатор, современный tcmalloc() тогда тоже еще не завозили, и героическая борьба с memory leaks…

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

Вот честно сказать, если бы я в то время ушел «на руко-водящую работу», больше бы не программировал (ну кроме макросов в экселе), и мне бы вдруг! надо было бы отсобесседовать молодежь — я бы вспомнил именно про алгоритмы :-)

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

А вот когда я грозно бы спросил про о-большое в случае реализации ArrayList на вставку в середине — я бы и произвел нужное впечатление на молодняк, и хотя бы смог бы понять их ответ :-)

Information

Rating
Does not participate
Registered
Activity