Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
MS Small Basic в настоящее время является лучшим учебным текстовым языком программирования. С появлением современной среды программирования SB-Prime, библиотеки LitDev и ряда других библиотек он получил великолепные возможности, позволяющие писать на нём сложные, полезные и интересные программы, которые можно использовать не только в учебных проектах, но и для решения вполне серьёзных повесдневных задач, однако, ему присущ ряд недостатков, делающих его непригодным для создания действительно серьёзных, а члавное - ответственных проектов, что долвольно обидно.
Переменные в MS Small Basic не требуют объявления. Здорово? На первый взгляд — очень! Ничего не нужно заранее продумывать, даже следить, чтобы переменная получила значение до её использования — всё равно получит что-то типа нуля. При этом переменные как бы сами «решают», какого они типа — в зависимости от того, что в них записано. Это кажется очень удобным, особенно — для учебного языка программирования. Но если копнуть чуть глубже, мы увидим, что далеко не всё так здорово, как кажется на первый взгляд.
Как можно реализовать такие простые и умные переменные? Реализовать их можно либо созданием специального класса, который сам определяет тип передаваемых данных, хранит их в одной из подходящих внутренних переменных, выполняет проверки на корректность и преобразования данных в случае необходимости, либо… Либо так, как придумали в Microsoft.
Разработчики MS Small Basic решили проблему создания неких «универсальных» переменных, подходящих для хранения любых типов данных очень специфическим (зато простым и быстрым) способом. Начать с того, что переменные в MS Small Basic все(!) — действительно одного единственного типа: и числовые, и символьные, и текстовые, и даже массивы(!). Фантастика? Нет. Решение, найденное разработчиками, конечно в принципе имеет право на существование, но оно довольно примитивно и несёт с собой кучу неудобств при работе. Я бы даже сказал, разработчики поленились сильно напрягаться — они просто придумали хранить любые переменные как… (барабанная дробь!) …текстовые строки! Настоящие программисты, я думаю, в шоке, как от простоты идеи, так и от потенциального количества проблем, которые она за собой тащит. Ну и от ленивости разработчиков, я полагаю, тоже… :)
Итак, что же мы имеем? Мы имеем довольно неуклюжий способ уйти от проблем при создании класса умных переменных. Зато у нас есть «умные» строки, которые при каждой операции распознаются или переписываются в новую область памяти… («Да чёрт с ней, с оптимизацией!»)
И эти же «умные» строки могут хранить в себе числа. Ну, в целом, да, действительно могут — в текстовом представлении, что, впрочем, можно сделать в ЛЮБОМ языке программирования — ну какая разница, что хранить в строковых переменных, особенно в «умных»? Текст — он и в Африке текст, а распознать его можно — как угодно…
Как производится «разбор» этих «умных строк»:
Это массив? Если да — то значит у нас есть массив «умных» строк, требующих разбора.
Это число? Да. Ну, тут, вроде, всё просто.
Если не 1. и не 2. , то это — просто текстовая строка.
В зависимости от того, можно ли преобразовать переменную в число или нет, она будет рассматриваться как число или текстовая строка. Таким образом, все следующие примеры — это сложение чисел:
TextWindow.WriteLine("3"+"4")
TextWindow.WriteLine(3+4)
TextWindow.WriteLine(3+"4")
А вот это — уже строки, поскольку «3a»
или «:»
не могут быть преобразованы в числа.
TextWindow.WriteLine("3a"+"4")
TextWindow.WriteLine(3+":"+4)
Зато мы легко можем увидеть, как выглядит «умная» строка с массивом, выполнив следующие команды:
a[1] = "hello"
TextWindow.WriteLine(a)
Кроме того, все остальные, в том числе и графические объекты, хранятся в Small Basic в виде «умных» строк, поэтому выполнив эти команды, вы увидите в консоли, например, идентификатор эллипса:
ellipse = Shapes.AddEllipse(20,20)
TextWindow.WriteLine(ellipse)
Поэтому стандартную операцию перемещения нашего эллипса:
Shapes.Move(ellipse,50,50)
Мы можем заменить на:
Shapes.Move("Ellipse1",50,50)
или на
Shapes.Move("Ellipse"+1,50,50)
Ничего не изменится! Теперь вы видите, какой простор для появления недиагностируемых ошибок оставляют такие вот «простые и классные» переменные без типа. А ведь Small Basic в первую очередь - учебный язык программирования. Он должен быть расчитан на предотвращение потенциальных ошибок учащихся, а не на их практически неконтролируемое размножение.
А если перейти к более серьёзным задачам, - тут же встает вопрос правильного выбора типов переменных или создания собственных типов, но SmallBasic сделает всё за вас — так, как ему больше «нравится»…
Такая или подобная концепция была бы хороша для какого-нибудь языка программирования, предназначенного для лентяев, не желающих вникать в суть программирования и фактически плюющих на конечный результат. Но применять подобное решение в учебном языке программирования?! Расчёт на детишек-глупышек, которые всё равно ничего не поймут, а все ошибки за них будут исправлять "умные" взрослые (в том случае, если смогут найти эти ошибки)?
В MS Small Basic в принципе отсутствует проверка корректности данных и их преобразований — как явных, так и неявных. В результате мы запросто можем записать вместо числа в переменную кусок текста или, выполнив случайно "неявное" преобразование число -> текст -> число (или наоборот), получим совсем не то число, которое было в начале.
Можете попробовать, например, создать переменную со значением 345876 (число, да?), а затем определить её длину как текстовой строки (Text.GetLength()) и вы получите — 6! «Чёрт возьми, Холмс! Но как?!…» Ведь это же число!
Приходит в голову только одна логичная (как ни странно!) мысль — писать все (ВООБЩЕ ВСЕ!) константы в программе как тестовые строки - в кавычках… Но это уже — полный бред! Хотя, наверное, должно сработать… и тем самым вообще уничтожить у учащихся понимание, что такое переменная.
Если у нас есть две переменных A и B со значениями 3 (число «три») и б0 («бэ» и «ноль» — ну так сложилось: опечатка в файле), то при сложении их в MS Small Basic мы получим 3б0 — нет, не число 360, а строку из трёх символов: «три», «бэ» и «ноль», а при вычитании — получим число «три», потому что текстовая строка, не содержащая чистого числа интерпретируется в MS Small Basic как число 0. И ни единого сообщения об ошибке! Здорово?! И это — учебный язык программирования!
А что делать, если две строки, содержащие числа нужно не сложить, а просто соединить? Ведь для склейки строк в MS Small Basic есть красивая операция «+»… Так вот она — не сработает! Она «тупо» сложит два числа (потому что приоритет сложения выше приоритета склейки). Нет, тут разработчики «вывернулись», придумав функцию гарантированной склейки Text.Append(), однако, создать, например, функцию определения типа переменной (вернее, хранящейся в ней данных) — почему-то забыли…
Если же мы захотим вывести число, содержащее десятичную точку в строку, например, используя Text.Append(), то точка «волшебным образом» может «превратиться» в запятую — в зависимости от региональных настроек Windows, и — при «переходе обратно к числу» в программе (переменная-то — одна) — мы вдруг обнаружим, что наше число стало нулём! Потому что в самом MS Small Basic десятичный разделитель — только точка, и, следовательно, строка цифр, содержащая десятичную запятую — числом не является, а значит = 0 (по правилам MS Small Basic).
Что можно сделать? Можно аккуратно написать функцию, которая будет пробегаться по всем элементам проверяемой строки (посимвольно) и сравнивать их с цифрами или с десятичной точкой, разумеется, учитывая при этом, что точка в числе может быть только одна. Или можно просто вычитать переменную, проверяемую на число, из нуля: операция «вычитание» со строками не работает, нечисловая строка интерпретируется как число 0, поэтому если результат вычитания останется нулём, то в проверяемой переменной — либо число 0, либо какой-то текст:
' a - проверяемая переменная
If 0 - a = 0 Then
'а - не число или 0Else
' a - число
EndIf
Также, для того, чтобы гарантировать, что у нас точно число, а не текст, на него очень похожий, можно умножать переменную на 1: число не изменится, а строка — превратится в 0. Вроде просто… но глупо до невозможности! Но… если этого не делать, то в результате сложения можно получить — всё, что угодно!
Хуже обстоят дела с проверкой переменной на соответствие формату времени или даты: дело в том, что формат задаётся пользователем в настройках Windows и может меняться. Тут видится только один надёжный способ: вывести в какую-нибудь переменную текущие время или дату с помощью функций класса Clock: Date или Time. Затем выполнить посимвольный анализ полученной строки, понимая, что основное содержимое строк — это цифры, а то что между ними — разделители, соответствующие текущему установленному в Windows формату даты или времени. Теперь, основываясь на этих данных анализа, уже можно анализировать любую другую строку на предмет соответствия формату.
А еще в MS Small Basic у нас есть «массивы» с кривой индексацией: ведь индексами массива может быть всё что угодно, а далеко не только числа. Более того, порядок и следование индексов (даже числовых) — вообще не обязательны: это же просто строки! И если вы, создавая массив, укажете значения для 1, 2 и 4 элементов, пропустив 3-й, то… в вашем массиве будет всего 3 элемента(!), а индексом 3-го (по порядку) элемента будет 4-ка(!): то есть, проходя по массиву циклом For от 1 до 4, вы вообще не встретите элемента с индексом 3(!) — просто потому что его вообще не существует, зато если вы пройдете по массиву циклом For от 1 до длины массива (Array.GetItemCount), то вы никогда не увидите элемента с индексом 4(!), потому что фактическая длина массива — 3. А всё потому, что это не массив в привычном программистском представлении, а просто длинная текстовая строка вида «1=13;2=123;4=98», которая каждый раз(!) обрабатывается специальными функциями текстового поиска и лексического анализа. Ужас? Да не то слово!
И ладно бы еще создали для подобных массивов что-то похожее на функционал очень удобного оператора цикла ForEach (последовательный перебор по всем существующим элементам массива), существующего во многих современных языках программирования. Действительно, наличие такого инструмента избавило бы от многих проблем при работе с такими вот «умными» массивами. Но нет, на это, к сожалению, чего-то не хватило: то ли квалификации, то ли денег, то ли времени…
Остаётся единственный вариант — принудительная полная «инициализация» массива перед его использованием или изменением размера — прямой записью в цикле в каждую последовательную ячейку какого-нибудь «начального» значения, например нуля или пробела, что сразу сводит на нет по крайней мере одно из преимуществ такого «умного» массива.
А что MS Small Basic вытворяет со стеками? Если вы используете один стек — вроде всё работает, но стоит вам использовать 2 разных стека (с разными именами) и «инициализировать» их, например, пустыми строками (зачем это нужно - выходит за рамки данной статьи) — и эти стеки «волшебным образом» склеятся в один! Поэтому перед первым использованием переменным с именем стека необходимо присваивать какие-то, желательно различные, значения: например, разные текстовые строки.
Кстати, никого не смутило, что стек нужно инициализировать путем присваивания значения его имени? Тут не то, что о логике — о здравом смысле говорить не приходится! Воистину: "если в программе завёлся баг - не спешите исправлять его, а просто обзовите фичей"...
Только понимание всего вышеизложенного позволит избегать в программах на MS Small Basic непонятных и неожиданных результатов и ошибок.
Разумеется, такая реализация сказывается на быстродействии - в минус...
Ну тут Microsoft — просто, как обычно, в своём репертуаре:
«Если наши программы на вашем компьютере работают медленно, купите себе новый более мощный компьютер«.
— Программы работают?
— Да.
— И в чём проблема?
— Медленно работают.
— А нам — плевать! Это — ваша проблема...
Ну действительно, ведь проект-то — бесплатный. Зачем же напрягаться с оптимизацией, осмыслением самого языка (даром, что он - учебный)?
Как вам, например, ещё вот такой «перл»: после условия в операторе If обязательно нужен Then, а вот в операторе While — никакого оператора, закрывающего условие — не предусмотрено! Где тут логика? А ведь она так необходима именно в учебном языке программирования! Ну что стоило хоть как-то упорядочить синтаксис, продумать сам язык до начала его программирования?
А библиотеки классов? Возникает ощущение, что кто-то просто резвился — как детёныш шимпанзе в вольере, создавая их: ни логики, ни осмысленности, ни привязки к насущным требованиям — налепили кое-как первое, что в голову пришло: для каких-то задач функционал — откровенно избыточный, а для каких-то - его попросту не хватает (и заменить нечем)… Единственная надежда — на библиотеку LitDev, в которой тоже — местами «не все так просто»…
Такое ощущение, что язык MS Small Basic писался разными студентами кого-то из сотрудников Microsoft второпях: «давай-давай быстрее!» — «сделай как-нибудь, лишь бы работало!» — «надо успеть!» — «бегом-кувырком!»…
Ну что сказать? — «Успели»!…
Выпустили безусловно очень нужный, но крайне сырой и корявый продукт, который в последствии за все годы развития даже не удосужились доработать.
В итоге получилось как обычно: «хотели как лучше, а получилось — как всегда» - отличная идея разбилась о «скалы» человеческой лени, глупости и жадности... И это очень печально. Ведь MS Small Basic мог стать действительно мировым лидером среди учебных языков программирования.
Единственный вывод из всего сказанного: на этом языке можно писать что-то серьёзное, только понимая и осознавая все эти нюансы и подводные камни.
PS: А если честно, то что-либо хоть сколько нибудь серьёзное — надо писать на С или С++, ну в крайнем случае - на C#. Там, по крайней мере, есть уверенность, что программа будет вести себя именно так, как написано — без двусмысленностей и разночтений.
В статье использованы материалы MSDN.