Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |
уравнения А-ИКС-квадрат плюс Бэ-ИКС плюс Це равняется нулю ( A*X^2+B*X+C=0 )
с конкретными А, Бэ и Цэ, которые нам дадут потом.
(Да: по-мне, так это тоже банально. Но искать например корни кубического
уравнения по формуле Кардано — громоздко. Да и не общеизвестно: в школе этого
обычно не проходят.)
Инструмент — интерпретатор Фокала. Это один выполняемый ДОС`овский файл
foc.exe и к нему еще один текстовый файл foc.hlp (или foc-1b.hlp) — справка.
Он же — основная документация. Его наличие не обязательно, но оператор Help
не будет работать.
Еще более необязателен файл _fpks.txt, но если он есть, то в нём сохраняются
между сеансами работы интерпретатора последние введенные пользователем строчки.
А в файле sav.f интерпретатор обычно предлагает сохранить программу. И если
он есть — при запуске пытается загрузить её обратно. (Но только если в
командной строке при запуске интерпретатора на этот счет ничего не указано.)
В оные времена Фокал работал на "голой" машине и был сам себе операционной системой. Самое близкое к этому — ДОС, который сидит себе в уголочке памяти, предоставляет файловый сервис и больше ни во что не вмешивается. Всё остальное Фокал делает сам или через функции БИОС`a. Версии под другие ОС планируются после отработки основных концепций и получатся они только ограниченные: например функцию FX реализовать там не представляется возможным...
Впрочем, ДОС`овские программы и под виндой работают — до XP включительно.
Запустим интерпретатор любым способом. Например набрав в командной строке
операционной системы: foc
Что видим: черный экран и на нём * (звёздочка). Это интерпретатор ждёт ввода
очередной командной строки. Хотя сперва мог выдать некоторое "приветствие".
(Чтобы не выдавал — см. в файле справки — строки этак с двадцать пятой: там
еще написано "сам себе ини-файл"...)
Вошли — сразу выйдем, набрав оператор Quit и нажав кнопку ввод. (Достаточно
просто Q.)
Но можно было нажать кнопку ЕСЦ. Тогда, если в памяти есть несохраненные
строки, интерпретатор предложил бы их сохранить, сформировав командную строку,
в конце которой — всё тот же оператор Quit. Её можно малость отредактировать,
например стерев это Q в конце. Или изменив имя файла, в котором сохранять. Или
вообще отказаться, еще раз нажав ЕСЦ — командная строка просто станет пустой.
И тогда по следующему ЕСЦ интерпретатор завершит свою работу.
Quit это в любом Фокале, а ЕСЦ — только в данной реализации.
Вышли — запустим по-новой.
Самой первой всегда пишут такую программу, которая бы проявила хоть какую-то
видимую активность. Например написала на терминале "Ха!" Или "Привет, люди!",
или еще какую подобную фразу, но на буржуйском языке. (С русскими буквами
бывают проблемы. В том числе злонамеренно созданные...) А для микроконтроллера,
у которого никакого терминала нет — хотя бы светодиодом помигала, припаянным к
одному из его выводов...
Написать:
Type 2*2
конечно тоже можно, но лучше всётаки:
Type "Привет, люди!", !
Второй восклицательный знак — тот, что за пределами кавычек, организует нам
переход на следующую строку. А всё, что в кавычках — выводится один в один.
(Якобы как пояснение к выводимым числам.) Запятую, которая разделяет эти два
элемента списка вывода, впринципе можно и не писать: и кавычки и
восклицательный знак и пробелы (которые можно вставлять в любых количествах)
сами по себе символы-разделители.
И, да: если нам нужно чтобы в тексте были кавычки — заключаем его в кавычки
другого типа, благо их три: " ' `
Но мы же хотели программу! Поэтому пишем:
1.1 Type "Привет, люди!", !
А потом:
Go
Или Do; или Go All; или Go 1; или Go 1.1; Эффект будет один и тот же.
Впрочем, нет: если вдруг в каталоге был файл sav.f а мы и не заметили, то в
памяти будет его содержимое, и уж что оно сделает... Вот если бы написали
Do 1.1
то точно бы выполнилась только и исключительно строка с номером 1.1, а вот
по другому... (Go — просто передача управления, а Do — подпрограмме, "взаймы".)
Поэтому перед вводом программы часто пишут Eraze Ales; (разумеется E A вполне
достаточно) чтобы в памяти уж точно ничего "лишнего" небыло.
А мы возьмём и проверим:
Write
или:
Write A
Во втором случае перед текстом программы будет добавлен заголовок — у данной
реализации в виде двух строчек комментариев: номер версии и текущие дата/время.
Вот кстати полезный оператор Comment весь остаток строки после которого
просто игнорируется. Предназначен для написания пояснений. Но можно например
взять и "закомментировать" ненужную пока програмную строчку, просто дописав в
её начале букву Ц — чтобы потом, когда понадобится, заново не вводить. Если
текст программы набирается отдельно — с помощью текстового редактора, то
сделать это ничего не стоит. А вот удалить, а потом вспоминать, что в
удалённой строке было написано, да еще и не ошибиться при её повторном наборе...
Но и в Фокале есть оператор Modify чтобы исправлять уже имеющиеся в памяти
строки, а не набирать их заново.
Ну ладно, побаловались и будя. Решаем уравнение A*X^2 + B*X + C = 0
для чего нам дали три конкретных числа и сказали что вот это и есть А, Бэ и Цэ.
Элементарно — через "дискриминант": D = B^2 — 4*A*C; и если D>=0
то x1= (-A + корень(D))/(2*B)
а x2= (-A — корень(D))/(2*B)
Все эти действия можно проделать и на калькуляторе и на линейке
(логарифмической) и просто с помощью карандаша и бумаги, и даже на счетах.
Вот только корень... Хотя раньше в школе учили и корни тоже вручную вычислять.
(А вот нас — уже нет.)
Но вот следующей задачей будет: решить не одно такое уравнение, а сорок
тысяч штук! Типа кто-то выдаст нам файл, содержащий сорок тысяч комплектов по
три числа в строчке. И вот тут уже бумажкой с карандашом не обойдёшься...
Поэтому не ленимся — пишем:
Set A=.....; Set B=.....; Set C=.....
где вместо точек — конкретные, данные нам числа
Set D = B^2 — 4*A*C; Type D
и если число D — неотрицательное, то:
Type (-A + FSQrt(D))/(2*B)
Type (-A — FSQrt(D))/(2*B)
всё — для одного конкретного набора чисел задачу решили.
Так сказать "единичную" задачу. А теперь решим "массовую": сделав из этого
подпрограмму. Предусмотрительно поместим её не в первую, а например в пятую
группу — просто напишем в начале строчек номера. Но пусть всётаки параметры
А, Бэ и Цэ будут не константами, а пусть программа каждый раз просит ввести их
с терминала. Для этого нам понадобится оператор ввода Ask:
Ask A,B,C; или даже: Ask "введите А, Бэ и Цэ: ", A,B,C
Пишем:
5.1 Ask "введите А, Бэ и Цэ: ", A,B,C
5.2 Set D = B^2 — 4*A*C; Type "Д = ", D
5.3 Type " X1,X2 = "
5.4 Type (-A + FSQrt(D))/(2*B)
5.5 Type (-A — FSQrt(D))/(2*B) !
Восклицательный знак в конце — чтобы на следующую строчку переходило.
В нашей реализации есть пул (мешок) для хранения ранее введённых строк. Доступ — стрелками вверх и вниз. Так что скоре всего ничего набирать заново не придётся, только номера в начале дописать. А если что, то и прямо с экрана стащить можно...
Ага. Но если дискриминант вдруг получится отрицательный, то при попытке
вычислить из него корень — будет ошибка: программа остановится, и
интерпретатор заругается. Впринципе сойдёт, но всё же лучше сделать чтобы оно
само проверяло сей факт и писало что ни будь типа: "корней нет".
Для этого нам понадобится оператор условной передачи управления: If(X)a,b,c;
Он сравнивает значение X с нулём и для трёх случаев, когда оно меньше нуля,
равно и больше — передаёт управление на строки с номерами a, b и c. Здесь
запятые — обязательны, скобки — впринципе тоже. А вот эти самые a,b,c могут
быть вовсе не константами, а выражениями произвольного вида.
Кстати, в правильном, полноценном Фокале произвольное выражение может быть
везде, где по смыслу требуется число. В том числе, вот как в данном случае, в
операторах перехода (передачи управления) Go, Do, If — получается "вычисляемый
переход", аналогичный например оператору switch языка Си. И даже когда ввода
ожидает оператор Ask — он тоже вводит вовсе не константу, а ищет в полученной
текстовой строке выражение и вычисляет его. (Ну калькулятор же!)
Константа — только номер в начале нумерованной (косвенной) строки, да еще в
таинственной конструкции "формат" оператора Type.
Формат указывает как выводить числа: ширину поля и количество значащих цифр: %N.M, или это может быть одиночный символ % восстанавливающий формат по-умолчанию. Заметим, N.M это не одно дробное число (как в номере строки) а два целых. Потому что например 7.10 и 7.1 это одно и то же число,
но если написать что: Set pi=3.14159265358979324;
то Type %7.10 pi выдаст 3.141592654
а Type %7.1 pi выдаст 3
Итак, рассматриваем условный оператор
If(X)a,b,c
В "базовом" (1.0) Фокале последние из a,b,c могут быть опущены, и тогда в
соответстующих случаях выполняется остаток строки. А в "продвинутом" (1.5)
можно пропустить любые — при условии сохранения запятых. Тот же эффект даёт
отрицательный номер строки. Нулевой — тоже не ошибка, а предписание ничего не
делать. Всё это — чтобы можно было использовать оператор If вообще без каких
либо реальных номеров строк. Например:
If (X) 0, ,0; Type "икс равен нулю"
If (X) ,0 ; Type "икс не равен нулю"
Так как a,b,c — могут быть какие хотим выражения, в том числе и такие у
которых запросто могут быть "побочные эффекты" (например изменятся значения
некоторых переменных), то вычисляется только одно из них. Остальные — просто
пропускаются.
Да, условный оператор Фокала (по сравнению например с Бейсиком) не очень
удобен: сравнивать два числа приходится вычитанием одного из другого. Операции
сравнения > >= < <= = и <> (не равно), как там — куда нагляднее. Но разрешить
их только в условном операторе, как это там и было сделано — нарушение
принципов! (Да и неоправданное усложнение грамматики, а значит и
программы-интерпретатора — бесцельный расход ресурсов, которых и так в обрез.)
А разрешить их везде — так они потащат за собою "логический" тип значений, а
он — еще и дополнительные "логические" операции, не применимые больше ни к
чему. (А за ним и другие типы потянутся...) И всё это ради чего? Ради пошлого
удобства и наглядности... Нам такого счастья не надо!
К тому же бинарная логика — ущербная. Фокал ненавязчиво приучает к
трёхзначной. Или даже к "нечеткой". Но вот логические операции таки нужны.
И если за "инверсию", "отрицание" сойдёт смена знака, то на роль И и ИЛИ в
(1.4) ввели функции FMIn и FMAx просто выдающие самый маленький и самый
большой из своих аргументов. С них кстати и началась эта эпопея (1.7) с
синтаксическим сахаром — что мол надо разрешить функциям иметь произвольное,
какое угодно количество параметров. Де лишние они пусть просто проигнорируют...
Но вернёмся к нашим баранам. Итак, добавляем:
5.25 If(D) 6.1
6.1 Type "корней нет" !
И всё — "массовая" задача решена.
Надеюсь, всё понятно?
И то, что номер строки это вовсе не два целых числа (как склонны думать
некоторые, бейсиком ушибленные), а одно вещественное? Поэтому строчка с
номером 5.25 не окажется в конце группы, а встанет между 5.2 и 5.3
Обратим внимание: наша программа расползлась на две группы. Если запускать
её командой Go, то группа 6 тоже будет выполнена — вслед за группой пять,
чего нам совсем не надо. Поэтому либо запускаем Do 5; либо в конец пятой
группы помещаем еще одну строку с оператором Quit, останавливающим программу.
Но он же остановит нам её и после обработки первой же строки из сорока
обещанных тысяч. Поэтому от 5.6 Quit; воздержимся, a таки напишем: 5.6 Ret
Этот оператор возврата из подпрограммы мы просто забыли написать. Или даже
скорее поленились: понадеялись на то, что группа строк (да и отдельная строка)
и без того "естественная подпрограмма" — возврат управления происходит по
достижении её конца автоматически — даже если там нет оператора Ret. Но это
когда обращение идёт к группе (или к одной строке). А у нас тут запускается
"вся" программа, вот и...
Можно констатировать факт, что и следующая задача тоже уже решена процентов
на восемьдесят. Чего там не хватает? Двух вещей: переключения каналов ввода и
вывода на файл с исходными данными и на тот куда поместить результат. А так же
цикла по строчкам этих файлов.
С циклом всё просто: у каждого файла есть указатель чтения/записи. Сразу
после открытия он в самом начале. А при каждом чтении (или записи) смещается
на размер прочитанного (или записанного). Так что вполне достаточно нужное
число раз вызвать нашу подпрограмму в группе 5:
3.7 Do 5; Go 3.7
и всё.
Такой цикл не завершается никогда. Но в данном случае программа не
"зациклится": как только строчки в файле закончатся — произойдёт ошибка
"конец носителя" и программа остановится сама собою. Что нам и надо.
(В смысле: сойдёт для сельской местности.) И даже каналы ввода и вывода сама
собою на терминал переключит. (Правда заругается.)
А открытые файлы при выходе из Фокала сами собою корректно закроются.
Но сначала их таки надо открыть и переключить на них каналы ввода и вывода:
3.1 Op "файл_с_данными" XR; Op X
3.2 Op "файл_под_результат" YW; Op Y
Вот собственно и всё.
А всё остальное у нас уже написано. Вот правда надо бы скопировать в файл
результата еще и исходные данные (а то непонятно к какой задаче ответ).
Добавим:
5.15 Type A,B,C
И вот теперь действительно всё — вторая задача тоже решена. Ну разве что
вписать правильные названия файлов с исходными данными и результатом. И,
разумеется, запустить.
Ну и конечно объяснить, что мы такое сделали. Поэтому еще немножко теории.
У Фокала есть один канал ввода и один канал вывода. Оперирует ими оператор
Operate, где вторым ключевым словом указывается куда такой канал переключить.
Из канала ввода читает оператор Ask (построчно), функция FCHR (побайтно),
ну и сам интерпретатор, когда ему нужна очередная командная строка (тоже
построчно). А в канал вывода пишет оператор Type, функция FCHR и еще оператор
Write. Вот такая у Фокала простенькая система ввода/вывода.
Оператор Modify позволяющий редактировать уже находящиеся в памяти строчки
формально тоже к ней относится. Но не использует каналы ввода/вывода, а
напрямую общается с пользователем — через терминал и клавиатуру. И потому
сильно зависит от реализации.
Предыдущая глава |
↓ Содержание ↓
↑ Свернуть ↑
| Следующая глава |