пятница, 29 января 2021 г.

Разобрался с синтаксисом

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

И вот, что я насоображал по поводу синтаксиса.

Во-первых, нужен будет стек. Чтобы просто не объявлять и не придумывать названия локальным переменным. Но я ещё не решил, скорее всего это будет FIFO, а не LIFO. Почему-то мне кажется, что это удобнее.

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

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

Во-вторых, и это самое главное, что я понял. Что у переменной метод get - это вызов переменной при обращении к ней как к параметру. Его не нужно вызывать явно - в этом просто нет смысла, хотя конечно это можно сделать, чтобы поместить результат на стек.

А вот метод set - это вызов функции, а не вызов переменной.

Таким образом синтаксис получается очень простым:

имя_вызываемой_функции  параметр_функции

И всё? - не поверил я. А как же всё остальное? Дополнительные параметры и всё такое.

Придётся что-то изобретать, как обойти данное ограничение. Когда я писал четвёртую версию fpli, то мне потребовался метод substr для класса String, который в Си имеет несколько параметров, и на первый взгляд кажется, что без них не обойтись - нужно задать начало и конец подстроки.

Но посмотрим на эту задачу иначе. У нас есть строка, нам нужно удалить начало и удалить конец - это не одна, а две операции, и так мы получаем искомую подстроку. 

Получится что-то вроде такого кода:

"Hello our perfect World!" // Кладём строку на стек
$ trim_left 10  // Удаляем 10 символов слева            
$ trim_right 7  // Удаляем 7 символов справа

На стеке остаются строки

0: "Hello our perfect World!"
1: "perfect World!"
2: "perfect"

Дело в том, что trim_left и trim_right - это методы класса String, и им не требуется больше одного параметра.

Но как же быть с методом convert_token? - возникает вопрос. Получается, что мы можем возвращать несколько значений результата. Ведь параметром функции может быть только литерал, константа, переменная или свойство объекта или класса. Чтобы передать в качестве параметра ссылку на класс или функцию, нужно будет добавить соответствующее ключевое слово - иначе просто непонятно, как быть в случае, если вызов функции возвращает указатель на функцию. Мы должны её вызвать или она и есть значение параметра.

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

четверг, 28 января 2021 г.

Сделал функции exec и calc_hash

 Занялся переделкой, и сделал функции exec и calc_hash. Снова моя программа умеет вычислять хэши строк.

Вычисление хэшей строк с помощью функций calc_hash и exec

Как ни странно, но удаление стека сильно упростило код. Я теперь описываю параметры функции, читаю их из консоли согласно описанию, а затем вызываю функцию, передавая ей эти параметры как массив значений класса Value.

В коде функции просто беру их из этого массива, и всё это очень нативно для C++, а потому очень компактно.

Теперь думаю, не добавить ли стек. Это было бы удобно. Если функция возвращает значение, и пользователь не кладёт её в переменную, то результат можно было бы помещать на стек.

Но такое поведение получается неправильным. Если нас не интересует результат вызова функции, то при чём здесь стек?

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

Честно говоря, я несколько удивлён: за день сделать exec и calc_hash - это неплохой старт. Конвертер токена строки я просто взял из предыдущей версии, поскольку нет смысла снова всё это писать с нуля.

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

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

С параметрами функций у меня появились и указатели. Поскольку например функция  convert_token возвращает не только результат конверсии, но и признак того, насколько конверсия прошла успешно. А это две переменных; поэтому одна из них возвращается как результат вызова функции, а другая изменяется через указатель, передаваемый как параметр.

В принципе что-то вроде указателей (ссылки) у меня было и в четвёртой версии. Поскольку иначе было не сделать операцию инкремента.


среда, 27 января 2021 г.

Решил, что нужно всё переделать

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

Получилось вот что:

$ + $ - работает
переменная + переменная - работает
константа + константа - работает

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

2 + 2 - это не работает

Нужно написать

2 - положить значение на стек
$ + 2 - сложить 2+2 и положить результат на стек

При этом, если рассматривать эту программу как калькулятор, то мы ожидаем, что 2 + 3 даст на стеке результат - 5. Но получилось, что на стеке останется 2 и добавится 5.

Работа программы с переменной типа Float

Нормально ли это? - спросил я самого себя.

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

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

Потому что 2 + 2 это либо эквивалентно

2.add(2) => стек

Либо эквивалентно

add(2, 2) => стек

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

2 => стек
стек.add(2) => стек

И поэтому первая двойка как бы и не должна удаляться. Лежит себе на стеке и лежит.

Я понял, в чём причина этой ошибки - в логике обработки ввода.

Мне нужно сделать так: взять строку, извлечь команду, взять параметры, выполнить команду, положить результат на стек.

А у меня сделано так: я выделяю объект, применяю метод, который берёт параметры по мере выполнения, и сам кладёт результат на стек.

Стек при этом получается как бы просто переменной особого рода. Он как бы непонятно для чего нужен в принципе. Вот, в Python его же нет, а здесь как бы тот же Python, только через стек нужно с ним работать.

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

Может возникнуть вопрос, в чём именно кривизна. Кривизна в данном случае в адресации объектов на стеке. Допустим, что до выполнения команды мы имеем на стеке число. Его адрес $0. А после выполнения команды его адрес уже $1, а $0 - это адрес результата.

С точки зрения Форта - это нормально. Но с точки зрения читаемости кода это будет просто адский треш:

принтер1
$0 команда1
$1 команда2
$2 команда3

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

Тогда возникает вопрос, зачем нужен стек, если есть переменные и константы. И получается, что стек только всё запутывает.

Подытоживая всё это, я пребываю в размышлениях.

понедельник, 25 января 2021 г.

Сделал класс COut и функцию вывода в поток

 Долго думал, как реализовать возможность использовать в качестве выбранного объекта и переменную, которые хранятся в таблице заголовков, и значение со стека - наконец понял, что мне нужен отдельный класс выбранного объекта SelectedObject, который сам по себе абстрактен, а для работы непосредственно с объектом используется одна из его реализаций - либо SelectedRecord, либо SelectedValue.

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

Использование класса COut для вывода текста на терминал

Думал, что будет сложно, но всё оказалось довольно просто, зато сам код функций-методов заметно упростится. 

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

Отлично в том смысле, что всё происходит как будто само собой. Нет каких-то специальных случаев использования, которые мне пришлось бы программировать дополнительно. Даже значение переменной копируется само - я лишь добавил функцию 'data_size' в класс COut.

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

$ + $

То есть: выбранный объект - значение с вершины стека, к нему прибавляем значение с вершины стека, то есть само себя. И результат кладём на стек.

Как это будет работать, пока неочевидно. Может заработать само по себе, а может придётся придумывать реализацию.

Планирую добавить адресацию вида $0, $1, $2 и т.д., чтобы не заставлять пользователя городить огород из операций по перестановке данных на стеке. Просто $ будет синонимом $0.

суббота, 23 января 2021 г.

Сделал функцию exec

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

Не проще ли реализовать это через функцию exec? -  спросил я самого себя.

Функция exec - это функция, которая в качестве параметра принимает строку и исполняет её как код интерпретатора. Как если бы мы в консоли этот код ввели.

Программа Hello World с помощью функции exec

Я впервые познакомился с концепцией функции exec, когда изучал JavaScript. 

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

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

Так же и в данном случае, вместо кода на C++ мне гораздо удобнее иметь возможность выполнить код на языке интерпретатора.

Функция exec оказалась достаточно простой. Мы кладём строку на стек и исполняем код "exec  $" - то есть исполнить код, заданный строкой на вершине стека.

Сделал sizeof и глобальные переменные

 Немножко конечно трудно работать, когда тебе мешает ФСБ, но в этом году выборы, и ФСБ с прошлого лета пытается меня прессовать. Всё же кое-как удалось выкроить время и что-то поделать.

Начал делать переменные, и сразу потребовалось сделать функцию sizeof.

Помимо этого сразу понадобились модификаторы для функций и свойств классов. Добавил модификаторы Regular, Static, Abstract, Virtual.

Пока язык напоминал объектный Форт, статичные функции не требовались, поскольку я клал при вызове метода this на стек, и получались по сути обычные фортовские функции - берёт данные со стека, кладёт результат на стек.

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

Переменная PI типа Float

Возник вопрос, можно ли использовать имя переменной как параметр функции.

Переменная set Другая_переменная

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

И сразу получается, что из функции get_param может происходить вызов другой функции (ведь у свойств есть метод get), а та может снова вызвать get_param и т.д. То есть код сразу станет нечитаемым, поскольку будет непонятно, где конец одного оператора и начало другого. Следовательно потребуется ввести ';' как оператор разделения операторов.

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

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

Теперь думаю переходить к определению функций.

понедельник, 18 января 2021 г.

Сделал конвертеры типов для констант

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

Определение констант со значениями другого типа

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

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

Потом смотришь - все варианты вроде работают, но со временем вдруг выясняется, что один из вариантов работает неправильно. Начинаешь исправлять - съезжает что-то ещё. Сидишь, ломаешь голову, размышляя, а как оно должно быть на самом деле.

В случае со старыми алгоритмами даже в отладчике не так-то просто понять, а что там вообще происходит, в этой функции в 150 строк.

У одного коллеги была функция размером в 2 Мегабайта. Это было конечно эпично.

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

воскресенье, 17 января 2021 г.

Добавил константы

 Начал добавлять переменные, но решил добавить константы, и добавил.

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

Константа создаётся строкой вида

Тип_константы constant Имя_константы Значение_константы

Поэтому, если например тип указан Long, а значение маленькое, то оно кладётся на стек с типом Short например, и его нужно в Long сконвертировать. А такие конверторы у меня сейчас сделаны только для типа Integer.

Пример констант PI и ten

Если бы я писал интерпретатор на ассемблере, то этой проблемы не было бы. Но я пишу программу на C++, а у него строгая типизация, и для работы с данными приходится писать функции, которые конвертируют один сишный тип в другой.

Можно было бы конечно прибегнуть к чисто сишным приёмам, копируя значение с помощью memcpy(), но этот фокус сработал бы только при конвертировании интегральных типов, а например конвертацию float в int уже пришлось бы делать так же, как у меня сделано сейчас. Так что я решил не заниматься усложением себе задачи.

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

Я не знал, нужен ли мне класс типа Class. Пока что не понадобился. Но уже оказалось удобным ввести классы WholeNumber - как признак, что это целочисленное значение, и класс Referenceable - как признак того, что этот класс может быть типом константы или переменной.

Меня уже спросили, зачем писать подобную программу в XXI веке. Это же как бы из эпохи мамонтов реликт.

Да, мне тут уже рассказали, что по технологиям это 1965-й год. Очень, очень давние времена.

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

В целом, моя идея в том, чтобы написать нечто среднее между Forth, Python и Visual Basic. Если посмотреть на каждый из этих языков, то при всех своих очевидных достоинствах, у них есть и недостатки.

Форт заставляет пользователя класть параметры функций на стек. Это немного странно в наше время, поскольку интерпретатор может сам читать параметры команды. А пользователю привычнее синтаксис вида

Переменная = Функция Параметры

То есть что-то вроде Бейсика. Только без номеров строк.

Visual Basic - это язык Microsoft Office, и его не назовёшь открытым решением. Python гораздо привлекательнее в этом смысле.

Ну, а Python практически лишён недостатков, кроме того, что ты не можешь начать писать программу на C++, встроив в неё Python как язык скриптов. Я же как раз пытаюсь сделать именно такое решение - вот код, вот стек, вот консоль.

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

Люди пишут, что интерпретатор Форт можно написать за вечер. У меня правда не получилось написать интерпретатор Форт за вечер. Но я и не пытался поставить в этом деле рекорд.

В общем, добавил константы.

пятница, 15 января 2021 г.

Сделал функции get_param и forget

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

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

Появление персонального компьютера всё изменило. Поначалу они не могли тягаться с большими ЭВМ по производительности, но уже к началу 1990-х в индустрии больших ЭВМ наметился кризис, который пережили очень немногие компании.

И вот, наступила эра ПК - у каждого на столе появился компьютер, а затем появился и Интернет.

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

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

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

Получился довольно прибыльный бизнес. Такие классы назвали Интернет-кафе.

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

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

И первая функция с параметром, которая это использует - это функция forget. Она позволяет удалять объекты из словаря объектов. 

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

Работа функции forget - из словаря объектов удалён класс Long

Чтобы функция могла различать, удаляется ли объект по индексу или по названию, мне пришлось написать конвертеры токенов для разных типов. А поскольку принимает она в качестве параметра либо Integer, либо String, то я добавил класс WholeNumber (целое число), для того, чтобы можно было сконвертировать значение на стеке - из Short в Integer или из Long в Integer, и т.д.



четверг, 14 января 2021 г.

Сделал конвертеры литералов

 Наконец-то доделал конвертеры литералов.

FPLI4 с конвертерами литералов

В процессе работы обнаружил и исправил баг в коде из предыдущих версий. Что показалось мне странным - ведь этот код вроде бы работал до этого.


понедельник, 11 января 2021 г.

Сделал LongLong

 Делаю конвертеры. Это функции, которые берут со стека токен и пытаются преобразовать его в литерал данного типа.

Вдруг выяснилось, что тип long long на платформе INT64 имеет размер 64 бита - такой же, как у long. А не 128 бит, как я предполагал.

Я поинтересовался, как обстоят дела в Питоне - там long long - это 128 бит. Впрочем, Питон умеет работать и с 256-битными числами.

Меня конечно такое положение не могло устроить. Моя таблица типов выглядит так:

  • Char - 8 бит
  • Short - 16 бит
  • Integer - 32 бит
  • Long - 64 бита
  • LongLong - 128 бит

В общем, пришлось написать собственную реализацию типа LongLong.

Пару дней я его писал, несколько дней исправлял ошибки, и вот, наконец-то он заработал.

Тип данных LongLong (128 бит)

Теперь можно приступать к конвертерам чисел с плавающей точкой.

понедельник, 4 января 2021 г.

Вычисление хэшей

 И снова зима, и снова очередной интерпретатор.

Два года назад я решил написать простой интерпретатор Форт-подобного языка. 

За четыре месяца я его написал. Пришлось переписывать четыре раза, но с четвёртого раза я таки его написал.

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

Чтобы понять, насколько это плохой или хороший результат, представьте себе язык ассемблера, в котором можно определять собственные функции, классы и даже свойства с методами get и set. 

И всё это работает конечно на порядки медленнее, чем например Python, но 1 МГц у этого процессора есть по скорости.

В принципе я потом проводил эксперимент с Питоном. В подобном тесте он показал похожую производительность. Просто некоторые моменты в нём очень хорошо оптимизированы.

Год я отдыхал, затем занялся дальнейшими переделками, и теперь работаю над восьмой или даже девятой версией, надеясь, что с ней неразрешимых проблем уже не будет. Хотя что мне остаётся - мне остаётся только надеяться.

Сегодня я научил программу вычислять хэши строк. Полезная функция.

Программа вычисляет хэши


Но я вспомнил, что самая первая версия начиналась примерно с этого же. Это побудило меня написать этот пост.

Вот, снова зима, снова интерпретатор. Снова программа умеет считать хэши.

За годы разработки программ я сделал такой вывод: если хочешь написать большую программу, пиши её медленно. Условно говоря, по одной функции в день.

Тогда мозг не перегружается информацией, и нет стремления всё успеть.

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

Но конечно самую серьёзную демотивацию я почувствовал, когда программа таки заработала.

Вот, новый язык программирования, объектный Форт, но писать на нём что-либо нет никакого желания.

А тут он снова перестал работать, всё надо переписывать, и быстродействие всего 1 миллион операций в секунду. 

Я почувствовал тогда какую-то непередаваемую пустоту. Как будто сделал нечто никому ненужное, как будто записал альбом музыки, которую никто не услышит.

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

Дело в том, что на Форте программировать довольно увлекательно. Сравнимо с Питоном.

А вот писать что-то такое с объектами и классами почему-то не возникает желания совершенно.

Я стал анализировать причины, почему это происходит. Почему на C++ писать интересно и легко, а на производном - на интерпретаторе, писать нечего.

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

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

И вот, она умеет вычислять хэши для строк. Чем не повод повеселиться.


пятница, 1 января 2021 г.

Сайт Vanyamba uses Linux в 2020 году

У поисковой машины Altavista был удивительный алгоритм сортировки сайтов.

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

Но если по ссылке не кликали пользователи, то страница смещалась вниз довольно быстро - ведь постоянно появлялись новые страницы.

Тем не менее, пол-дня ваша страница могла провисеть на первом месте в поиске.

Это давало такой трафик на сайты, что основной проблемой стало то, что большинство серверов просто не могло обработать столько запросов.

Когда аудитория Altavista стала прирастать со скоростью 1 миллион человек в месяц, этот алгоритм пришлось изменить. Большинству сайтов столько трафика оказалось попросту не нужно.

Интернет-бум быстро разрушил предтечу Интернета - сеть FidoNet.

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

Но бурный рост Интернета сразу позволил таких бизнесам резко увеличить оборот. Появился даже новый термин - онлайн-торговля.

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

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

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

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

Но я подумал вот о чём. Этот покупатель, которому не достался последний серверный модем, ведь он предложил за него 4.5 цены, и всё равно ему не хватило денег. Мне стало интересно поговорить с ним об этом.

Он рассказал довольно интересную вещь об онлайн-торговле.

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

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

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

Посещаемость сайта Vanyamba uses Linux хорошо иллюстрирует эту мысль.

На первом месте в 2020-м году он был только по запросу "как подключить к". Но такой запрос в Google был всего 1 за весь год.

С другой стороны три самых популярных запроса были "eof", "по спи" и "stdin", которые дали в сумме только 3.64% кликов на сайт. Срелний CTR по этим запросам был всего 0.19%.

В 2020-м году больше искали "stm32 linux" и "stm32 pwm", так что в принципе можно было бы что-нибудь написать об этом, но это совсем другой уровень владения предметом в области программирования микроконтроллеров.

Мне пришлось бы рассказывать, как взять микроконтроллер stm32 и сделать на нём небольшой проектик. А такую возможность предлагала только плата Olimexino-STM32.

По теме Arduino сайт Vanyamba uses Linux пользователи Google искали в 2020-м году по запросам "генератор сигналов на ардуино", "ардуино таймер задержки" и "arduino мигание светодиодом без delay". Срелняя позиция сайта в поиске - 4.3, а CTR при этом составил 11.77%.

По числу просмотров страниц за 1 сеанс или за несколько, лидируют всё равно пользователи одного сеанса - 13170 против 5653, но вернувшиеся читатели читают больше страниц на сайте - 1.76 против 1.6.

При этом прочитали больше 10 страниц 105 человек, а больше 25 страниц - всего 9.

Интересно выглядит отчёт Вовлечение:

Вовлечение аудитории сайта Vanyamba uses Linux в 2020-м году
Если посетитель почитал текст на сайте больше минуты, то он прочитает несколько страниц. А если больше 3 минут, то и ещё больше. Значит, не зря пишу.

Из новых интересных проектов по Arduino, появившихся в 2020-м, могу порекомендовать канал в YouTube Заметки Ардуинщика.