понедельник, 23 декабря 2019 г.

Столько не живут...


четверг, 14 ноября 2019 г.

Просто мысли вслух. О функциональщине и императиве

В продолжение темы:

https://programmingmindstream.blogspot.com/2019/11/blog-post.html?m=1

Чем дальше, тем больше - проникаюсь идеями "функциональщины" типа Haskell.

А именно:

1. Константность "объектов". Слово "объекты" - не зря взял в кавычки.
2. Детерминированность результатов функций. Если f ( x, y ) = ( x + y ), то f ( 1, 2 ) - ВСЕГДА вернёт 3. Что бы ни случилось!
3. Кешируемость результатов. Она вытекает из детерминированности.
4. Распараллеливание вычислений. Оно также вытекает из детерминированности.
5. Оперирование не результатами, а функциями. f ( 1, 2 ) на "самом деле даёт" не 3, а @ f ( 1, 2 ), то есть "ссылку на выражение, связанное с параметрами".
6. Из этого вытекает, что программа становится не "последовательностью" императивных инструкций, а деревом "выражений", констант и ссылок на них. И в процессе выполнения программы это дерево "схлопывается" до конечного результата функции main. Ну это грубо говоря. Ввод/вывод Я не беру - это отдельная тема. "Те самые" монады.
7. А из этого вытекает "ленивость вычислений".
8. Ну и кроме всего прочего - "контейнеры". С map, filter, join, sortedJoin, take, intersect, sort, fold, diff etc. Контейнеры при таком подходе можно рассматривать либо как "строки алфавита" если контейнеры не сортированные, тогда все операции над контейнерами переводят "строки одного алфавита" в строки другого. Либо как "множества", если контейнеры сортированные. Тогда мы именно над множествами, которые нам знакомы из теории множеств.
9. Кортежи. Тут не буду особо комментировать. Просто они важны. Они отличаются от скаляров и множеств. Они скорее - "аналоги векторов". Это собственно основа ADT (https://www.ohaskell.guide/adt.html).
10. Операции над функциями как над данным. Связывание функции с параметрами, ссылки на функции, полное и неполное применение функции. Уменьшение арности функции, оно же "каррирование", то есть фиксация одного из параметров функции.
11. Вообще - трактование всего функционала программы не как "последовательность инструкций" (императив). А как преобразование "вход" -> "выход".
12. И в итоге получается совсем другой взгляд на программы. Получается, что программа это функция, которая оперирует скалярами, векторами, и множествами и преобразует "вход" в "выход".
13. Ну и "почти забыл". Но это немаловажно. Трактовка "контейнеров" как структуры head:tail. Голова-хвост. При этом head это "скаляр" относительно данного контейнера, а tail это такой же контейнер, но на элемент меньше. При этом немаловажно понятие [] - пустого контейнера. Аналог 0 для скаляров.
14. Да. Ещё "моноиды". Просто их упомяну. Потому что "вроде понимаю", но сказать не могу. Это множества с операцией и "единичным" (или "нулевым", зависит от трактовки) элементом. Примеры - ( 0, + ), ( 1, * ).
15. Совместимость "простого" типа и выражения, которое "возвращает данный тип":
 INTEGER VAR X = 1 - ну тут понятно.
 INTEGET VAR Y = @ ( 1 + 2 ) - тут "тонко". На самом деле в Y положится не 3, а ссылка на выражение, которое вычислится ТОЛЬКО когда из Y станут читать значение.
Это очень ВАЖНЫЙ аспект.
16. Автоматический вывод типа выражения. Тоже - просто упомяну. Тут тоже очень долго рассказывать.
17. ДОСТАТОЧНОСТЬ операции < (СТРОГО МЕНЬШЕ) для множества задач. А НЕ Compare, которая возвращает -1, 0, +1.
Это тоже - очень тонкий момент.
18. Нуль-арные конструкторы как "константы типа". У контейнеров "пустой контейнер" или [] - это собственно и есть один из нуль-арных конструкторов. Значение, которое означает "только само себя" и ничего более. Тут с точки зрения императивного программиста прослеживается "аналогия" с Enum.  Но только именно "аналогия", и именно в "кавычках". Из нуль-арного конструктора нельзя получить ничего более как СРАВНИТЬ с "самим собой", тогда получим true, или сравнить с другим значением "того же типа", тогда получим false. Тут "спинным мозгом чую, что поллитра". Шучу. А серьёзно - я чую, что это связанно с "моноидами".
19. Любое выражение типа void - имеет "побочные эффекты". Иначе ему незачем быть. Это либо ввод/вывод, либо какой-то другой "императив", а не "функциональщина". Тут тоже видимо просматривается связь с "монадами". Чую что пол-литра, а доказать - не могу.
20. Про "ленивость" вычислений я уже писал? Тут тоже есть тонкий момент:
 INTEGER VAR X = @ ( 1 / 0 )
- не вызовет исключения ZeroDivide, в момент присвоения "значения" переменной X (на самом деле это не переменная, а "алиас" к выражению из дерева разбора).
Исключение будет только если мы попытаемся "читать" X, например напишем что-то вроде:
 if (X == 2)
или
 X Print
21. Тотальное использование рекурсии. Особенно для обработки "контейнеров". Причём в лучшем случае - это "хвостовая рекурсия", то есть БЕЗ переполнения стека. Когда предыдущее значение заменяется следующим.
22. Всё вышеперечисленное приводит к тому, что функционал программы сводится к "алгебре" над значениями (скалярами, векторами и множествами) и "комбинаторике" над функциями (применение к операндам, возврат ссылки, вычисление значений, сравнение etc). Ввод/вывод или random или GetTickCount - не берём. Но они выводятся за рамки данной схемы.
23. Детерминированность вычислений также приводит нас к тому факту, что можно например сравнивать не значения, а выражения:
 f ( x, y ) = ( x + y )
 f ( 1, 2 ) == f ( 2, 3 )
 достаточно сравнить не ВЫЗОВ функции на реальных значениях, а лишь выражения:
 @ f ( 1, 2 ) == @ f ( 2, 3 )
Для скаляра это не очевидно, а вот для "контейнеров" - вполне очевидно:
 [ 1 2 3 ] filter IsOdd == [ 1 2 3 ] filter IsOdd
- понятно, что в данном случае для сравнения не надо перебирать контейнер.
24. Эквивалентность значений не всегда тождественна:
 Boolean Equals ( a, b ) = ( not ( a < b ) and not ( b < a ) )
Тут тоже "тонкий момент". На подумать.
25. Лямбды. Как я про них забыл.
26. (подобнее о пункте 23) выражения "эквивалентнны" если "эквивалентны" их операнды. Тут тонко:
 ( 1 + 2 ) == ( 1 + 2 ) - равны значения
 @ ( 1 + 2 ) == @ ( 1 + 2 ) - равны значения и выражения
VAR X = 1
VAR Y = 2
 ( X + Y ) == ( X + Y ) - равны значения
 @ ( X + Y ) == @ ( X + Y ) - равны значения и выражения
VAR X = 1
VAR Y = 2
 ( X++ + Y ) == ( X++ + Y ) - НЕ равны значения
 @ ( X++ + Y ) == @ ( X++ + Y ) - НЕ равны значения, НО равны выражения
Или:
VAR X = 1
VAR Y = 2
 ( X++, X + Y ) == ( X++, X + Y ) - НЕ равны значения
 @ ( X++, X + Y ) == @ ( X++, X + Y ) - НЕ равны значения, НО равны выражения.
Выражения/функции (связанные или не связанные с операндами) - это значения несколько "иного порядка", чем скаляры, векторы и множества (проще говоря - константы). Над выражениями несколько иная "алгебра" нежели чем над "константами". Что в общем и "понятно", но далеко не всегда "очевидно".
Но таким образом и "код программы" превращается в "алгебру над константами", только там "константами" выступают "выражения".
То есть "сущности более высокого порядка".
И в итоге эти "(функциональные) константы" в результате работы программы схлопываются к ОДНОЙ "(нефункциональной) константе", которая собственно и есть - "результат работы программы".
Мы естественно не берём GUI или "интерактивный" ввод/вывод. Или random или GetTickCount.
Но с ними - "другая история", которая тоже обобщается на эту концепцию. Через "границы разделов", суть - Монады.

Как-то так.

Причём это всё не "сухая теория" из книжек, а принципы которые я осознал и применяю в своём "повседневном программировании".

Естественно это всё написано с точки зрения программиста на ИМПЕРАТИВНОМ языке. И это ни на что не претендует.

Жаль только, что я опять "изобретаю велосипед" и иду тем путём который до меня прошли многие. Но возможно в этом и состоит "программистское Айкидо". Пока сам не "сделаешь"- не поймёшь - что же другие имели в виду.

Зато на этом подходе я сделал уже как минимум три различных фунционала:

1. Генерация кода по модели.
2. Индексация текстов и поиск.
3. Преобразование форматов текстов.

И всё в итоге сводится к операциям над множествами, скалярями и векторами.

Этот список "размышлений" похож на:
1. http://programmingmindstream.blogspot.com/2014/01/blog-post_27.html?m=1
2. http://18delphi.blogspot.com/2013/11/gui_9423.html?m=1

Вообще говоря - я тут попытался пересказать книжку "О Haskell по-человечески" (https://www.ohaskell.guide/). Только словами "императивного программиста".

среда, 13 ноября 2019 г.

Ещё ссылка

http://roman.yankovsky.me/temp/delphispec/DM_DelphiSpec.htm

"Kerk ©   (20.12.13 20:34[26]

> jumping jack   (20.12.13 14:02) [22]
> как насчет "Delphi/Pascal --> Gherkin"? ;^)

О подобном Александр Люлин много пишет, у него там "переходники" между user-friendly скриптами и Delphi-кодом полуавтоматически генерируются:

http://programmingmindstream.blogspot.ru/2013/12/delphispec_20.html

Это все интересно, но по-моему DelphiSpec проще :)
"

Кому-то помогли мои изыскания "на коленке"

Кому-то помогли мои изыскания "на коленке".

Кто бы мог подумать...

Не зря "изобретал велосипеды"...

https://www.sql.ru/forum/1297701-2/perevod-proekta-na-64-bitnuu-platformu

"
: A galaxy far far away
Сообщений: 3396
к слову, настоятельно рекомендую добавить модуль в проект:
http://programmingmindstream.blogspot.com/2017/01/1333-64-integer.html
помогает локализовать потенциальные проблемы."

среда, 6 ноября 2019 г.

Сделал "абстрактные" контейнеры

В продолжение темы:

https://programmingmindstream.blogspot.com/2019/10/blog-post_5.html?m=1

Сделал "абстрактные" контейнеры.

У которых есть "базовые" абстракции:

1. GetEnumerator - перебор элементов от начала к концу.
2. GetRevertedEnumerator - перебор элементов от конца к началу.
3. CompareItems - функтор сравнения элементов. Вообще говоря - достаточно операции Less (строго меньше). Всё остальное из Less выводится.
4. Sorted - признак сортированности контейнера.

Из этих абстракций выводятся:

1. Enumerable - "фабрика" Enumerator'ов. Она же - "контейнер".
2. Enumerator - енумератор/итератор по элементам. С ответственностими:
 a). MoveNext: Boolean - сместиться к следующему элементу.
 b). Current: ItemType - текущий элемент.

У которых есть:

1. Join - конкатенация над множествами (подобно конкатенации строк):
 [ 1 2 3 ] join [ 1 2 3 ] == [ 1 2 3 1 2 3 ].
 [ 1 2 3 ] join [ 1 2 3 4 ] == [ 1 2 3 1 2 3 4 ].
2. SortedJoin - объединение множеств (дубликаты выкидываются):
 [ 1 2 3 ] sortedJoin [ 1 2 3 ] == [ 1 2 3 ].
 [ 1 2 3 ] sortedJoin [ 1 2 3 4 ] == [ 1 2 3 4 ].
3. Diff - вычитание множеств.
 [ 1 2 3 ] diff [ 2 ] == [ 1 3 ]
4. Intersect - пересечение множеств = комбинация SortedJoin и Diff .
  [ 1 2 3 ] intersect [ 2 ] == [ 2 ]
5. First - первый элемент.
 [ 1 2 3 ] First == 1
6. Last - последний элемент.
 [ 1 2 3 ] Last == 3
7. Empty - пустое ли множество?
8. Count - количество элементов.
 [ 1 2 3 ] join [ 1 2 3 ] == 6
 [ 1 2 3 ] sortedJoin [ 1 2 3 ] == 3
9. Equals - множества эквивалентны?
10. Compare - сравнение множеств - -1, 0, +1.
11. ToTheLeftOf (less) - одно подмножество "левее" другого (строгая операция Less).
 [ 1 2 ] less [ 3 4 ] == true
 [ 3 4 ] less [ 1 2 ] == false
 [ ] less [ 1 2 ] == true
 [ 1 2 ] less [ ] == false
 [ ] less [] == false
 [ 1 ] less [ 1 2 ] == false
 [ 1 2 ] less [ 1 ] == false
12. GetItem - элемент по индексу. В худшем случае - перебор по getEnumerator.
13. Reverted - инверсия:
 [ 1 2 3 ] reverted == [ 3 2 1 ]
14. Fold - свёртка:
 [ [ 1 2 ] [ 3 4 ] ] == [ 1 2 3 4 ]
15. Slice - выделение "пар/троек etc":
 [ 1 2 3 4 ] slice 2 == [ [ 1 2 ] [ 3 4 ] ]
 [ 1 2 3 4 5 6  ] slice 3 == [ [ 1 2 3 ] [ 4 5 6 ] ]
16. Filter - фильтрация:
 [ 1 2 3 4 ] filter IsOdd == [ 1 3 ]
 [ 1 2 3 4 ] filter IsEven == [ 2 4 ]
17. Map - преобразование элемента:
 [ 1 2 3 4 ] map ( + 1 ) == [ 2 3 4 5 ]
 [ 1 2 3 4 ] map ( + 2 ) == [ 3 4 5 6 ]
18. Has - наличие элемента.
 [ 1 2 3 ] has 1 == true
 [ 1 2 3 ] has 5 == false
19. Contains - наличие подмножества.
 [ 1 2 3 ] contains [ 1 ] == true
 [ 1 2 3 ] contains [ 1 2 3 ] == true
 [ 1 2 3 ] contains [ 1 2 3 4 ] == false
 [ 1 2 3 4 ] contains  [ 1 2 3 ] == true
 [ 1 ] contains [ 1 2 3 ] == false
 [] contains [ 1 ] == false
 [ 1 2 3 ] contains [] == true - любое множество ВКЛЮЧАЕТ пустое множество.
20. Intersects: Boolean - множества пересекаются:
 [ 1 2 3 ] intersects [ 1 2 3 ] == true
 [ 1 2 ] intersects [ 1 2 3 ] == true
 [ 1 2 3 ] intersects [ 1 2 ] == true
 [ 1 2 3 ] intersects [ 4 5 7 ] == false
 [ 1 ] intersects [ 2 ] == false

При этом контейнеры могут находиться как в памяти, так и читаться из "потока".

На этом механизме построена частичная индексация текстов.

суббота, 5 октября 2019 г.

Коротко. О множествах

Размышлял тут на тему моноидов и вообще "обобщённых операций" над множествами...

И "неожиданно" пришёл к выводу, что операции isNull (пустое множество) и isAll (всё возможное множество) - крайне полезны для реализации других операций над множествами.

Говоря "программистским языком" - они вообще синглетоны могут возвращать.

При этом:

isNull * isNull = isNull
isAll *  isAll = isAll
isNull * isAll = isNull

isNull + isNull = isNull
isAll + isAll = isAll
isNull + isAll = isAll

isNull - isNull = isNull
isAll - isAll = isNull
isNull - isAll = isNull
isAll - isNull = isAll
-----
isNull * X = isNull
isAll *  X = X

isNull + X = X
isAll + X = isAll

isNull - X = isNull
isAll - X = isAll - X
X - isNull = X
X - isAll = isNull

среда, 2 октября 2019 г.

Коротко. Юникод

Вчерне доделал индексацию и поиск в юникодных текстах. Даже формулы ищутся. Много всяких приколов вылезло. Особенно со смешением кодировок. Например есть "кириллический игрек Y". Или альфа вместо А в русских словах. Умляуты, там где не надо. Штрихи оформленные маленькими буквами I в виде надстрочного индекса. Ну и всякое такое. Запрограммировал всё в "функциональном стиле". Вход преобразуется функтором, ну и так по цепочке. Конвейер.

Сделал функцию - ParseByFunctors.
Принимает строку и набор функторов, которые эту строку преобразуют в другую строку или даже набор строк.

Детали может быть позже.

P.S. Например есть слово Yamaha - написано вроде так. А а не ищется. Начинаем разбираться - НИ ОДНОГО латинского символа там нет.
Есть кириллическая Y.
Кириллические A.
Татарская (!) h.
Ну и да - соврал. Один латинский - m.
ОДИН латинский - есть. Ну и тому подобное. А в документе - Yamaha.

А IIV. Римская цифра. Где одна из I это не латинская, а греческая буква.

А заглавная АЛЬФА вместо А?

Или вроде "греческая сигма", заглавная. Ан нет. Есть похожая ЛАТИНСКАЯ буква - называется почти как немецкая "эсцет".
"Латинская Эш" - вот она как называется.

А ещё например метры квадратные то м2 - с верхним индексом. То м и специальный символ - верхняя 2.

Или скажем текст "запылился". Искажения. И получилось Z с черточкой, а не просто Z.

Или слово дОлжным, где над О ударение в виде штриха. Так - в первоисточнике.

А такие штрихи ещё и разные бывают.

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

Или слово РОССИЯ, где русские только И и Я. Все остальные - латинские, а то и вообще греческие.

вторник, 10 сентября 2019 г.

Offtopic. Эссе про Айкидо

Центр Айкидо АЙКИКАЙ «Койнобори Додзё»
Эссе: «Почему я занимаюсь айкидо в клубе «Койнобори»

Текущая степень______3 кю_______год, когда была присвоена__2012______
Общий стаж занятий айкидо___14 лет___из них в Койнобори___14 лет______

Айкидо я занимаюсь с 2005 года с некоторыми перерывами. За это время моё личное отношения к занятиям Айкидо менялось и трансформировалось. Попробую описать текущее положение вещей и мой нынешний взгляд на занятия Айкидо.
Как ни банально звучит, но в данный момент в первую очередь я рассматриваю занятия Айкидо как деятельность сильно полезную для здоровья. При этом на рубеже 45 лет я пришёл к пониманию, что Айкидо можно заниматься по-разному. Можно занимать интенсивно  и резко, а можно вдумчиво и плавно. В зависимости от текущей физической формы. В детстве и юности я занимался классической борьбой около 10 лет. А также, на волне общей моды, около полутора лет карате. Так во сравнении скажем с борьбой или карате — по моему личному ощущению Айкидо сильно выигрывает в плане возможности варьирования стиля и интенсивности занятий учитывая текущую физическую форму.
Также для меня занятия Айкидо это самоорганизация и преодоление. Выстраивание режима дня. Режима жизни. Поскольку опаздывать на тренировки, а тем более пропускать их я считаю не комильфо, то приходится выстраивать свой график таким образом, чтобы везде успевать. И на работу и на тренировки, и на другие немаловажные дела.
После двух лет занятий у меня было ощущение, что «английский я уже знаю». Потом стало приходить понимание, что всё далеко не так просто. И многое совсем не лежит на поверхности. Чем дальше занимаешься, тем больше обращаешь внимание на детали и сложности.
И это хорошо, что многое сразу не получается. Хорошо, что далеко не всё сразу и легко даётся. Есть над чем работать. Трудности порождают интерес. Если бы всё легко давалось, то и ценность занятий была бы куда меньше. С каждым годом открываются всё новые и новые грани. Постепенно приходит понимание тех вещей о которых раньше не задумывался. Развитие по спирали.
Что же касается именно клуба Койнобори, то мне особенно  не с чем сравнивать. С другими инструкторами я мало сталкивался, равно как и с занимающимися из других клубов.
Но мне нравится заниматься в клубе Койнобори отчасти тем, что у клуба есть какая-то общая линия, более менее стандартизированный подход и общие требования. Но при этом у каждого из инструкторов, у которых я занимался или с которыми я пересекался на семинарах или тренировках, есть свой личный подход, в рамках общей «клубной традиции». И это очень здорово. С одной  стороны есть базис, на котором основано всё, а с другой — каждый инструктор приносит своё виденье и свои личные вариации.
Также немаловажным аспектом занятий в клубе Койноборе я считаю то, что регулярно проводятся семинары Секи сенсея. Каждый его семинар насыщен и интересен. У Секи сенсея очень открытый подход. Он вызывает искреннюю симпатию.
Я за время своих занятий в клубе посетил около 7 или 8 семинаров Секи сенсея. И каждый раз было что-то новое. Какие-то новые подробности, важные детали. Но при этом опять же в рамках определённого общего базиса. Постоянство с одной стороны, а также совершенствование техники с другой, очень подкупает.
Ну и также мне нравится, что инструкторы клуба подходят ко всем занятиям крайне серьёзно, и требуют серьёзности отдачи от занимающихся. Они настоящие подвижники своего дела.
В общем — хочется продолжать заниматься и далее и пытаться достичь какого-то качественно нового уровня в технике и форме.
Будем заниматься.

Дата  «24»___мая______2019 г                                                                 Город__Москва_______


ToDo. TArchiStorageTest переписать на скрипты

TArchiStorageTest переписать на скрипты.

А потом - TArchiStorageVersionTest.

Ну и "покрутить" с виртуальностью и "наследованием" тестов.

А также с эталонами.

И пред- и постусловиями.

Возможно вынести в постусловие сравнение файлов с эталонами.

Не знаю. Надо подумать.

среда, 21 августа 2019 г.

ToDo. Заменить где возможно memoryChain на TReadOnlyBuffer

Заменить где возможно memoryChain на TReadOnlyBuffer.

ToDo. Сделать константные буфера

Сделать константные буфера.
Которые пишутся только при чтении из файла. А потом "закрываются" через VirtualProtect.
Чтобы пользователь буфера не смог его испортить.
По аналогии с sharedConstString.

Типа:

var
 Buffer : TBuffer;
 Processor: TSomeMemPoolProcessor;
 aStream: IStream;
While true do
Buffer := TBuffer.Create(aStream);
// - создаётся буфер с фреймом из файла.
// там внутри VirtualAlloc и VirtualProtect.
// которые делают mem_readOnly
If Buffer = nil then Exit;
// - если ничего не прочитали, то выходим
Processor.DoBuffer(Buffer.AsPointer);
// - там буфер доступен только на чтение
Buffer.Free;
// - освобождаем буфер
end;// - переходим к следующему фрейму

Там естественно try..finally, чтобы буфер не тёк.

четверг, 15 августа 2019 г.

Хоккей с памятью и методами

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

Получилось что-то типа TVirtualInterface.

Только со связыванием в Compile-Time.
Почти. Но On Demand.

Про реализацию - чуть позже напишу.

Побочным эффектом получилось, что можно отдавать класс "как интерфейс", но без лишнего подсчёта ссылок. Делегат что ли это называется.

Грубо говоря у класса получаются ДВЕ реализации IUnknown, одна с подсчётом ссылок, а другая - БЕЗ оного.

Чем то схоже с Protocol в Objective-C.

Ну и DuckTyping там где-то рядом.

Класс может выдать себя в качестве "утки, которая умеет махать крыльями".

При этом он не является IDuck или TDuck.

Построено всё на перекрытии NewInstance и распределении дополнительной памяти "за объектом". И в этой дополнительной памяти "варим" виртуальные таблицы для каждого из "интерфейсов"/protocol'ов. По образу и подобию того как это в Delphi сделано, только эта таблица инициализируется "на лету" из метода FillProtocolVMT. Туда пихаются указатели на методы нашего объекта.

Можно в принципе вообще "на лету" варить такие таблицы, совсем "onDemand". Например в QueryInterface. Только я пока не придумал зачем.

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

Попутно я научился цеплять к каждому классу свой менеджер памяти. С возможностью кеширования только определённого количества экщемпляров. Ну и попутно научился делать память экземпляров immutable, через virtualAlloc и virtualProtect. Экземпляры таких классов реально immutable. Они могут модифицироваться только в конструкторах и деструкторах. А во всё остальное время жизни экщемпляров - они readOnly. Доступ к полям на запись, даже "случайный" вызывает pageFault.

То есть можно делать реально константные кешируемые объекты.

Ну это функциональными языками "навеяло".

Дальше можно делать expressionOnDemand. Выражения, вычисляемые и кеширумые по мере необходимости.

Правда вся эта конструкция пока несколько "шаткая" и до конца не отлажена, но ничего. Отлажу.

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

пятница, 9 августа 2019 г.

Просто о сложном. О жизни и оптимизации. И получении от этого драйва и удовольствия

А ещё я ускорил работу нашего хранилища. И его копирование вместо 3х НЕДЕЛЬ (!) стало занимать 4 часа.
Поправил буквально 5 строчек кода. Но это вряд ли интересно.
Алгоритм маляра конечно же.
Я бы написал на эту тему. Но это вряд ли уже интересно.