Система научных расчётов для мобильных телефонов frt-sci

Введение
Добавление новых програм
Из чего состоит frt-sci
Вычисления в frt-sci
Комментарии
Ввод/вывод
Если в программе ошибка
Циклы
Определение новых слов
Условная конструкция
Переменные
Работа с памятью
Графики функций
Стэк возвратов и передача управления
Особенности реализации
Философия программирования на форт-подобных языках («Forth way»)
Список слов frt-sci
Библиотека слов
Ссылки

Введение

Frt-sci является реализацией форт-подобного языка программирования для мобильных телефонов с поддержкой java. Программы для frt-sci составляются на компьютере, а затем загружаются в телефон (в текущей версии программы надо вставлять в мидлет и прописывать в меню). После запуска frt-sci вы видите меню выбора программ. При работе программы имеются две области — поле ввода и область вывода текста.

В данном руководстве описан язык программ frt-sci. Для изучения этого языка (запуска примеров) можно использовать консоль frt-sci.

После запуска консоли можно набирать программу. Простейшим примером программы может быть

2 3 + .

Эта программа состоит из символов "2","3","+",".", разделенных пробелами.

Вообще говоря, весь синтаксис форта определяется одной фразой: программа на форте состоит из слов, разделённых пробелами, и исполняется слева направо. Все отличия между разными фортами состоят в деталях реализации отдельных слов. Любая программа в frt-sci тоже состоит из набора слов, разделенных пробелами (и символами новой строки). Числа и символы вроде + и точки тоже являются словами и должны быть отделены пробелами от остальных слов.

Для выполнения программы выберите "Ok". Для данной программы результатом будет число 5.

Frt-sci ориентирован в основном на применение в качестве программируемого научного калькулятора. В нем используются числа с плавающей точкой (для работы с ними применяется библиотека Real, http://real-java.sourceforge.net/Real.html), имеется набор стандартных математических функций, конструкции для условного выполнения участков программы, для организации циклов. Можно определять новые функции. Можно использовать переменные и память для массивов, состоящую из 1000 ячеек. Кроме того, в frt-sci можно строить графики функций.

Отличия от стандартного Форта (для тех, кто с ним знаком)

Стэк работает с числами с плавающей точкой. По-другому работает интерпретатор: весь текст сразу компилируется в шитый код, поэтому циклы можно организовывать вне определений. Immediate-слова работают по-другому, например после скобки ( не обязателен пробел. Текст в кавычках выводится в поле результатов. Переименованы слова управляющих конструкций: if=[, else=][, then=], циклы { }. Текущее значение счётчика на каждой итерации цикла { } кладётся в стэк. Для циклов { } используется свой стэк, а не стэк возвратов. Нет create, does и др.

Добавление новых програм

В текущей версии есть два способа добавления программ:

  1. Скопировать её на телефон и открыть через пункт From file в меню (телефон должен поддерживать JSR75).

  2. Добавить её прямо в мидлет. Пусть программа хранится в файле program.frt:

  1. Распаковать мидлет (.jar - файл) с помощью любого архиватора zip.

  2. Скопировать в папку с распакованным мидлетом файл program.frt.

  3. Добавить название файла и название программы в файл menu.txt

  4. Запаковать все файлы в мидлет.

  5. Переписать мидлет на телефон.

  6. Если вашему телефону нужен .jad-файл, исправьте в нём размер мидлета.

В Линуксе удобно это делать в Midnight Commander'е, в нём можно не распаковывать а просто зайти в мидлет

Из чего состоит frt-sci

Эта система включает в себя:

Вычисления в frt-sci

Все операции в frt-sci проводятся с использованием стэка. Стэк представляет собой место для хранения и передачи данных. Его можно представить себе как «яму»: туда можно класть элементы и можно доставать их оттуда. Положить элемент можно только поверх остальных, а достать можно только верхний элемент. Про верхний доступный элемент говорят, что он находится на вершине стэка. В frt-sci в стэке хранятся числа с плавающей запятой.

Для записи арифметических выражений в frt-sci используется обратная польская запись. В этой записи аргументы предшествуют функциям, которые на них действуют. (Из этого правила имеется пара исключений, они будут оговорены отдельно.) Хотя такая запись может сперва показаться неудобной, она позволяет обойтись без скобок.

Например вместо sqrt(2) следует писать 2 sqrt. Операции, имеющие два операнда: вместо 2+3 следует писать 2 3 +

При обратной польской записи все слова выполняются строго последовательно. 2 это слово, + это тоже слово. Если слово является числом, при выполнении это число попадёт в стэк. В форте все слова сами выполняют возложенные на них действия, и ничего другого они делать не могут. Это значит, что в примере 2 3 + сначала в стэк попадет число 2, затем число 3, затем будет выполнена операция «плюс». «+» удалит числа 3 и 2 из стэка и положит на вершину результат. Если записать 2 3 + 2 * . , то при выполнении этой программы в начале в стэк попадет число 2, затем 3, затем они будут удалены оператором "+" и в стэк будет помещено число 5, затем число 2, затем "*" удалит 5 и 2 и поместит 10. Важно заметить, что в стэке может находиться гораздо больше чисел, чем нужно для данного слова. Лишние числа при выполнении слова не затрагиваются, поэтому тот же результат получится, если записать 2 2 3 + * ..

Для вывода одного числа из стэка на экран в frt-sci используется оператор "." (точка).

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

Если нужно вычислить что-то сложнее, чем просто вычисление по формуле, то бывает необходимо достать элемент не с вершины стэка. Для этой цели служат несколько слов:

swap: поменять местами верхний элемент и лежащий под ним. Для описания того, как данная операция меняет состояние стэка, элементы стэка записываются в ряд слева направо (элемент, находящийся на вершине – самый правый) до и после действия операции. Указываются только те элементы стэка, которые важны для данной операции, а не весь стэк. Для swap такая запись: ( n1 n2 – n2 n1 )

over ( n1 n2 – n1 n2 n1 )

rot ( n1 n2 n3 – n3 n1 n2 )

dup ( n1 – n1 n1 ) оно дублирует число на стэке.

Комментарии

Текст в скобках является комментарием.

(Это комментарий)

Ввод/вывод

Для вывода числа в стэке используется слово точка. Текст в кавычках выводится в поле результатов: "hello, world"

Чтобы обеспечить ввод пользовательских данных используется слово quit. Это слово всего лишь прерывает работу программы. Оболочка же устроена так, что после того как пользователь нажмёт Ok программа продолжит выполняться с того же места, где она была остановлена, только перед этим будут выполнены все введённые в поле ввода слова. Т.е. если в поле ввода ввести несколько чисел, после возвращения из quit они будут находиться в стэке. Например, программа

begin quit + . 0 until

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

Теперь допустим, что надо добавить в эту программу слово ?, которое будет печатать справку. Что надо сделать, чтобы не вызвать исчерпание стэка? Ответ такой: надо добавить в конце описания этого слова quit.

: ? "Справочный текст" quit ;

Если в программе ошибка

Если при компиляции встретится слово с незнакомым именем, в поле вывода появится сообщение об ошибке с указанием незнакомого слова.

Если в ходе выполнения программы произойдёт сбой (например, исчерпание стэка), программа будет сброшена и frt-sci вернётся в меню.

Средств отладки пока нету, планируется добавить декомпилятор, к счастью в форте это легко :)

Циклы

В frt-sci есть два вида циклов: со счетчиком и с условным выходом.

Для создания циклов со счетчиком служат фигурные скобки { }.

Левая скобка обозначает начало цикла со счетчиком (вроде for). Простейший цикл выполняется от нижней границы до верхней с приращением +1. { ( b a -- ) берет из стэка две границы цикла. Внутри тела цикла текущее значение счетчика кладется на вершину стэка. Слово } ( -- ) анализирует счетчик цикла и при необходимости осуществляет возврат к началу цикла. Пример:

5 0 { . }

напечатает числа от 0 до 5.

Более сложный вариант цикла – с шагом не равным +1. Здесь используется слово +{ ( b a d -- ), которое отличается от { тем, что берет с вершины стэка еще и приращение. Пример напечатает числа от 0 до 1 с шагом 0.1:

1 0 0.1 +{ . }

Если (b-a) не делится нацело на d, результат округляется.

Циклы с условным выходом создаются с помощью слов begin until. Beginначало цикла. Слово until ( n – ) анализирует число на вершине стэка, если оно не равно нулю происходит выход из цикла, если равно — повтор. Пример:

5 begin 1- dup . dup 0 = until

напечатает числа от 4 до 0.

Болше возможностей предоставляет цикл begin while repeat. Слова между begin и while будут выполнены всегда. Если при исполнении while число на вершине стэка не ноль, будут исполнены слова от while до repeat и произойдёт возврат к begin.

5 begin 1- dup while dup . repeat

напечатает числа от 4 до 1.

Определение новых слов

Для создания новых слов (функций) служат слова : и ; Форма использования слова : несколько отличается от того, что было описано ранее: название вновь создаваемого слова должно идти после : Пример:

: sq dup * ;

Слово sq возводит число в стэке в квадрат.

Этот же механизм может применяться при определении констант:

: Grav 6.67e-8 ;

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

func слово ( -- addr ) помещает в стэк адрес слова

exec ( addr -- ) исполняет слово, адрес которого в стэке

Пример:

func sin 1 swap exec .

Условная конструкция

задается словами [ ][ ]

[ ( n -- ) если n!=0 выполняет блок кода от [ до ] или до ][. Слово ] обозначает конец условной конструкции. Слово ][ разделяет блоки кода, исполняемые при n!=0 и n=0 и служит аналогом слова else. Пример

: abs dup 0 < [ 0 - ] ;

это слово берет модуль от числа на вшерине стэка.

Переменные

Переменные в frt-sci пока что могут иметь названия из одной латинской буквы a-z. Всего 26 переменных. Значение переменной присваивается с помощью слова to. Способ применения:

to имя_переменной

снимет верхний элемент со стэка и занесет его в переменную.

Если записать имя переменной без to, то в стэк попадет значение из переменной.

0.5 to a
a .

Работа с памятью

Если переменных для хранения данных не достаточно, можно использовать память из 1000 ячеек. Первые 26 ячеек совпадают с переменными a-z Слово ! записывает число в память.

! ( значение адрес -- )

Числа извлекаются из памяти словом @

@ ( адрес – значение )

Графики функций

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

Для построения графиков применяется следующая конструкция. Сначала словом graph ( b a — ) задается диапазон по оси абсцисс на котором будет строиться график. Словами plot имя-функции задаются одна или несколько функций, графики которых необходимо построить. Слово show собственно строит график заданных функций. Например:

pi 0 graph
plot sin plot cos
show

Следующий пример строит график десяти функций sin(x) ... sin(10x)

: ff a * sin ;
pi 0 graph
10 1 { to a plot ff } 
show

Слова graph и plot переключают вывод чисел и текста на графический экран, а слово show на обычное поле вывода.

Стэк возвратов и передача управления

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

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

В frt-sci стэк возвратов может хранить только целые числа. Поэтому его применение для временного хранения результатов ограничено.

Для работы со стэком возвратов предназначены слова:

>r ( n -- ) (r: -- n ) перемещает число из стэка данных в стэк возвратов, отбрасывая дробную часть.

r> ( -- n ) (r: n -- ) осуществляет операцию, обратную >r

r@ ( -- n ) (r: n — n ) копирует число из стэка возвратов в стэк данных.

ret ( – ) извлекает адрес из стэка возвратов и осуществляет переход.

При операциях со стэком возвратов нужно быть осторожным. Ошибки со стэком возвратов иногда приводят к почти непредсказуемой работе программы. Например:

: aa 1 >r ; aa 2 . (2 не напечатается)

Глубина стэка возвратов в frt-sci — 100 элементов. Стэка данных — 50. Стэка циклов со счетчиком — 30.

Особенности реализации

Философия программирования на форт-подобных языках («Forth way»)

Программисты на Форте обычно хвалят этот язык за то, что программы на нём получаются компактными, простыми, красивыми и за сроки гораздо меньше чем обычно принято у других программистов. Но у новичков часто программы получаются вовсе не такими. Есть языки программирования, которые как бы уравнивают всех программистов, т.е. программы написанные наиболее способными программистами мало отличаются от программ, написанных новичками. Форт же, по словам создателя языка Чака Мура, наоборот, является «усилителем» возможностей программиста: хороший программист может сделать с помощью форта фантастические вещи, а у плохого получится ужасная программа. Однако научиться писать на Форте хорошие программы не так сложно, как обычно кажется новичкам, достаточно лишь немного тренировки для уяснения операций со стэком, которые являются самой непривычной частью Форта. Благодаря простоте Форта для его освоения достаточно запомнить лишь небольшое количество основных правил.

Ввиду краткости данной инструкции все советы и примеры по написанию эффективного кода здесь привести нельзя, за подробными разъяснениями обратитесь, например, к книге Л.Броуди «Способ мышления — Форт» http://forth.org.ru/~cactus/files/brodie.rar

Приведу лишь несколько советов:

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

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

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

  4. Используйте стэковые комментарии (пример — в списке слов в конце инструкции).

  5. Если планируемуе вами слово должно брать из стэка более трёх параметров, оперировать этими праметрами будет чрезвычайно неудобно (слова swap, dup, over, rot не умеют заглядывать глубоко в стэк). В большенстве случаев можно перепроектировать слово так, чтобы такого количества параметров не требовалось. Например, для слова, численно берущего интеграл кажется необходимым передавать через стэк два предела интегрирования шаг и указатель на функцию — четыре числа. Гораздо проще вспомнить, что заменой переменных любую функцию можно свести к функции на отрезке (0,1), поэтому слово, берущее интеграл от 0 до 1 будет достаточно универсальным для решения любых задач и пределы интегрирования передавать не надо.

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

Частично проиллюстрировать форт-подход (советы 1-4) можно на следующем примере (это немного изменённый пример из книги Броуди). Пусть вам требуется рассчитывать стоимость телефонных звонков с поминутной оплатой. Причем тариф такой, что при расстоянии между абонентами меньше 100км в будни стоимость минуты днём 3 рубля, ночью 2 рубля, в выходные днём 2 рубля, ночью 1.5. При расстоянии больше 100км днём на 2 рубля дороже, ночью на 1. Если описать решение задачи «влоб» то получится вот что:

: СТОИМОСТЬ ( минуты — стоимость )
  Расстояние 100 < [
              Будни? [
                       Ночь? [ 2 * ][ 3 * ]
                     ][
                       Ночь? [ 1.5 * ][ 2 * ]
                     ]
              ][
              Будни? [
                       Ночь? [ 3 * ][ 5 * ]
                     ][
                       Ночь? [ 2.5 * ][ 4 * ]
                     ]
] ;

Предполагается что слова Будни?, Ночь? определены заранее и оставляют на стэке нужный флаг. Вроде выглядит не очень сложно (кстати, слово * можно было вынести за скобки), но при увеличении количества вариантов сложность будет нарастать катастрофически. Так, если плата за минуту разная в каждый день недели и в каждый час дня, это определение растянется примерно на 2х7х24=336 строк! Тут ещё программисту приходится заботиться о том чтобы отступы были правильными. А если начало [ и конец ] управляющей структуры не помещаются на одном экране, разобраться в таком коде очень сложно даже при наличии отступов. Разве что разработать специальный редактор, в котором можно «сворачивать» блоки условных конструкций. Такие редакторы, скажем, для Си реально существуют, и это пример того, как чтобы бороться с недостатками одной программы приходится создавать другую.

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

(Переменные a и b используются для хранения стоимости днём и ночью)
: БУДНИ/ВЫХОДНЫЕ 
    Будни? [ 2 3 ][ 1.5 2 ] to a to b ;
: ДАЛЕКО?
    Расстояние 100 > [ a 2 + to a  b 1 + to b ] ; 
: ДЕНЬ/НОЧЬ   ( – минута )
    Ночь? [ b ][ a ] ;
: СТОИМОСТЬ ( минтуы — стоимость )  
    БУДНИ/ВЫХОДНЫЕ ДАЛЕКО? ДЕНЬ/НОЧЬ * ;

В случае разных тарифов в каждый день и час решение задачи таким способом займет порядка 40 строк, хотя в этом случае рациональнее было бы просто сделать таблицу данных 24х7. В этом примере можно было обойтись и без переменных, но так чуть более наглядно.

Казалось бы, можно вовсе не вводить слова БУДНИ/ВЫХОДНЫЕ, ДАЛЕКО?, ДЕНЬ/НОЧЬ, а сделать всё то же самое внутри одного слова. Однако, осмысленное введение новых слов с коротким описанием (1-2 строчки) и названием, ясно говорящем о функции слова, делает программу легко читаемой и самодокументируемой. При изменении, скажем, наценок за расстояние сразу понятно куда надо вносить изменения. В Форте вводить новые слова легко, нет необходимости указывать какие параметры будут передаваться слову, как это необходимо в Си и Java при определении новых функций. Кроме того, на вызов слова из другого слова тратится очень мало вычислительного времени.

Список слов frt-sci

swap ( n1 n2 – n2 n1 )

over ( n1 n2 – n1 n2 n1 )

rot ( n1 n2 n3 – n3 n1 n2 )

dup ( n1 – n1 n1 )

{ ( b a -- )

+{ ( b a increment -- )

} ( -- )

begin ( -- ) отмечает начало цикла

until ( n – ) вернуться к begin если n=0

while ( n – ) тождественно [

repeat ( – ) возврат к begin

[ ( n -- ) переход к ], ][ или repeat если n=0

][ ( -- )

] ( -- )

! ( значение адрес -- )

@ ( адрес – значение )

>r ( n -- ) (r: -- n )

r> ( -- n ) (r: n -- )

r@ ( -- n ) (r: n — n )

ret ( – ) немедленно покинуть текущее определение

to имя-переменной ( n -- )

func слово ( -- addr ) помещает в стэк адрес слова

exec ( addr -- ) исполняет слово, адрес которого в стэке

> < >= <= = отношения, например:

> ( a b — 0 или 1 ) единица если a>b

not or and – логические операции

+ - * / -- арифметика

sin cos tan asin acos atan sinh cosh tanh asinh acosh atanh exp ln lg erfc abs -- математические функции одного аргумента. Для тригонометрических аргумент в радианах.

pi ( -- 3.14159... )

% ( x — 1/x )

rnd ( -- x ) случайное число от 0 до 1

1+ 1- 2* 2/ ускоренные арифметические операции

^ ( x y — x^y )

int ( x — n ) целая часть

. ( n -- ) печать числа

cr ( -- ) вставить перевод строки на экран результатов

bl ( -- ) вставить пробел на экран результатов

bye ( – ) выход в меню

format ( n – ) задать количество символов при выводе чисел (по умолчанию 8)

sleep ( n – ) подождать n миллисекунд

milliseconds ( – n ) число миллисекунд с 01.01.1970 00:00:00

randomize ( n – ) задать число для генератора случайных чисел

depth ( – n ) число элементов в стэке до выполнения depth

today ( – год месяц день )

now ( – часы минуты секунды )

norm sci eng ( – ) режимы представления чисел

Библиотека слов

В frt-sci имеется библиотека слов, определённых через двоеточие. Она хранится внутри мидлета в файле lib.txt. Библиотека компилируется при старте пользовательской программы. Вы можете помещать туда наиболее часто используемые слова.

Ссылки

Л.Броуди «Начальный курс программирования на языке Форт» http://elrond.tud.ttu.ee/material/kallik/C/Loengud/Forth/contents.htm

Л.Броуди «Способ мышления — Форт» http://forth.org.ru/~cactus/files/brodie.rar

Офицальный сайт российских фортеров http://forth.org.ru/

Hosted by uCoz