Урок 2. Стандартные синтаксические конструкции, операторы
Основной особенностью большинства языков программирования, как и одним из основных критериев отличия одного языка от другого, являются синтаксические конструкции. Язык 4Test в этом плане исключением не является. Поэтому, разобравшись с типами данных и способами инициализации, можно приступать изучению конструкций, в которых эти величины могут участвовать.
Подключение файлов
Полностью реализованная автоматизация для среднего размера приложения занимает десятки тысяч строк кода различного назначения, способа и интенсивности применения. Поэтому есть насущная необходимость распределять различные фрагменты кода по файлам. Различные файлы могут подключаться друг к другу, делая доступными различные компоненты, объявленные в этих файлах.
Подключение некоторого файла осуществляется при помощи оператора use, который имеет следующую структуру:
use <File name>
Здесь File name - это строка, содержащая адрес подключаемого файла. Может использоваться как относительный так и абсолютный пути. Относительный путь задается относительно текущего каталога, в котором находится текущий файл, запрашивающий подключение.
Для рассмотрения данного оператора, создадим inc-файл ( типы файлов и их создание описаны в разделе Работа с проектом, основные типы файлов, подготовка среды), в котором мы будем хранить определенные нами типы данных. Назовем файл Types.inc. Далее, для отражения текстов рассматриваемых примеров, создадим файл Lesson02.t. Когда созданы эти файлы, можно приступать к рассмотрению операторов.
Итак, для демонстрации работы оператора подключения файлов, подключим файл Types.inc в файл Lesson02.t. Подключение файлов желательно осуществлять в самом верху кода, соответственно, файл Lesson02.t начинается со следующего кода:
|
Code
|
[ ] // Lesson 2: Standard syntax constructions sample
[ ] use "Types.inc"
|
Итак, подключен файл Types.inc, находящийся в том же каталоге, что и Lesson02.t. Теперь в файле Lesson02.t можно использовать компоненты, объявленные в Types.inc. Путь к подключаемому файлу является относительным, так как задается относительно текущего каталога. Для демонстрации возможностей задания относительных путей подключим тот же самый файл, но немного по-другому зададим путь. Итак, допишем строку вида:
|
Code
|
[ ] use "..\Test\Types.inc"
|
Если проект был создан полностью исходя из действий, описанных в разделе Работа с проектом, основные типы файлов, подготовка среды, то наши файлы находятся в каталоге Test, который соответствует имени проекта. Что же мы подключили в последнем фрагменте кода? Читая слева-направо эту конструкцию, получаем, что мы подключили файл Types.inc, находящийся в каталоге Test, который в свою очередь является родительским каталогом для данного каталога. Теперь немного специфики. Данный каталог - это каталог Test. Таким образом мы второй раз подключили файл Types.inc. Что из последнего примера стоит вынести? Две точки вместо имени каталога осуществляют переход на один уровень вверх по иерархии каталогов, что дает возможность подключать файлы вне текущего каталога, используя относительные пути.
Еще одним немаловажным моментом является то, что при повторном подключении одного и того же файла не будет выполнено дублирования деклараций, хранящихся в данном файле. То есть фактически файл подключится только в первый раз, второе его подключение будет проигнорировано.
Также могут быть использованы абсолютные пути. Например, файл Types.inc находится в каталоге D:\Documentation\SilkTut\Test, тогда его можно подключить так:
|
Code
|
[ ] use "D:\Documentation\SilkTut\Test\Types.inc"
|
Поскольку сам путь для оператора use является строкой, то мы можем использовать конструкции, характерные для строк, в частности можно использовать вставки. Например, подключение того же самого файла можно описать в виде:
|
Code
|
[ ] const STRING ROOT = "D:\Documentation\SilkTut"
[ ] use "{ROOT}\Test\Types.inc"
|
Данный способ более приемлем, так как он позволяет отсечь общую часть для путей подключаемых файлов и отражать конкретную структуру проекта. Иными словами, если мы переместим файлы в другой каталог, нам достаточно будет скорректировать константу, чтобы нужные файлы находились корректно.
Научившись подключать файлы, мы можем отделить ресурсы от кода, который их использует, распределить все компоненты кода на логически взаимосвязанные составляющие, сгруппированные по определенному признаку и разместить по различным файлам, что упрощает чтение кода.
Арифметические операторы
Несмотря на то, что язык 4Test является специализированным языком программирования, в нем присутствуют арифметические операторы. Это, в общем-то неудивительно, так как при автоматизированном тестировании вполне могут проводиться какие-то математические расчеты с целью проверки корректности работы некоторого вычислительного алгоритма. Также могут проводиться расчеты и в более простых случаях. Все это обуславливает необходимость использования стандартных арифметических операторов.
Основные арифметические операции можно свести в следующую таблицу:
|
Оператор
|
Описание
|
|
+
|
Оператор сложения. Для числовых типов представляет собой обычную математическую операцию сложения. Для множеств - это аналог операции объединения. Для строк и списков - это операция конкатенации
|
|
-
|
Оператор вычитания, используемый в основном с числовыми типами данных. Также применим для множеств и аналогичен операции вычитания множеств
|
|
*
|
Умножение. Применяется только для числовых типов
|
|
/
|
Деление. Применяется только для числовых типов
|
|
%
|
Остаток от деления. Применяется для типа INTEGER и других целочисленных типов
|
|
**
|
Возведение в степень. Применяется для числовых типов данных
|
Исходя из вышеприведенной таблицы следует, что из всех операторов, операторы сложения и вычитания действуют не только для целочисленных типов данных. Тот же оператор сложения используется некоторыми типами как оператор конкатенации.
Приоритет выполнения данных операторов соответствует общепринятым соглашениям о порядке выполнения арифметических операций, характерным для большинства языков программирования.
Операторы сравнения
Как и в других языках программирования, если есть возможность проводить какие-то математические операции или другие действия, преобразующие значения переменных, то естественно возникает необходимость как-то сравнивать между собой полученные величины. Для этого и существуют операторы сравнения. Результатом выполнения данных операторов будет величина типа BOOLEAN. Имеющиеся операторы приведены с следующей таблице:
|
Оператор
|
Описание
|
|
>
|
Вернет TRUE, если операнд слева больше операнда справа
|
|
>=
|
Вернет TRUE, если операнд слева больше либо равен операнду справа
|
|
<
|
Вернет TRUE, если операнд слева меньше операнда справа
|
|
<=
|
Вернет TRUE, если операнд слева меньше либо равен операнду справа
|
|
==
|
Вернет TRUE, если операнд слева равен операнду справа
|
|
!=
|
Вернет TRUE, если операнд слева не равен операнду справа
|
Данные операторы применимы не только к числовым значениям, но и к другим типам данных по определенному критерию. Так, для строк данные операторы осуществляют проверку исходя из лексикографического порядка строк-операндов.
Логические операторы
Поскольку в 4Test-е присутствует такой тип, как BOOLEAN, то есть необходимость и в логических операциях, определенных для данного типа. Бинарные операторы:
- || - логическое "ИЛИ", результатом является TRUE, если хотя бы один из операндов является TRUE или приводится к такому значению по правилам преобразования типов
- && - логическое "И", результатом является TRUE, если хотя оба операнда являются TRUE или приводятся к такому значению по правилам преобразования типов
Также имеется унарный оператор ! ( логическое отрицание ), который обращает значение BOOLEAN-величины на противоположное. То есть если есть некоторая величина BOOLEAN bValue содержащая TRUE, то конструкция !bValue имеет значение FALSE и наоборот.
Побитовые операторы
При хранении величин, соответствующих некоторой маске атрибутов, возникает необходимость производить операции на уровне битов или выполнять преобразования побитово. В 4Test-е это применимо к целочисленным типам данных, в частности INTEGER. Имеющиеся побитовые операторы приведены в следующей таблице:
|
Оператор
|
Описание
|
|
|
|
Побитовое "ИЛИ". Устанавливает в результирующем значении 1 на некоторой битовой позиции, если хотя бы один из операндов на той же битовой позиции имеет 1
|
|
&
|
Побитовое "И". Устанавливает в результирующем значении 1 на некоторой битовой позиции, если оба операнда на той же битовой позиции имеют значение 1
|
|
^
|
Исключающее "ИЛИ". Устанавливает равной 1 каждую битовую позицию, в которой операнды имеют различные биты, и устанавливает в 0 каждую битовую позицию, в которой биты операндов совпадают
|
|
~
|
Обращает во всех битовых позициях 1 на 0 и наоборот
|
|
>>
|
Сдвигает левый операнд побитово вправо на количество позиций, указанное во втором операнде
|
|
<<
|
Сдвигает левый операнд побитово влево на количество позиций, указанное во втором операнде
|
Оператор @
Зачастую может случиться, что мы не знаем в общем случае с каким объектом нужно выполнить определенные действия, присвоить некоторое значение. Это может быть поле записи, это может быть переменная и много чего другого. Но при этом четко выработан механизм выполнения этих действий. Было бы удобно как-то параметризировать имена варьирующихся объектов, то есть хранить их имена в переменных, которые потом подставлять в выражения. Остается уяснить, как из переменной извлечь имя объекта и при этом указать, что его надо воспринимать не как значение переменной, а именно как имя объекта.
Для целей конвертации значения переменной в объект используется оператор @. У данного оператора один операнд - строка, которая содержит имя объекта и которую нужно разыменовать в объект.
От теории перейдем к практике. Попробуем, используя данный оператор, заполнить некоторые поля record-а, при этом имена полей содержатся в некоторой переменной. Поскольку мы подключили файл Types.inc для хранения собственных типов данных, то имеет смысл сейчас им воспользоваться. В этом файле мы объявим record, который хранит в себе пару "ключ-значение". Итак, в файл Types.inc впишем строки:
|
Code
|
[+] type Pair is record
[ ] STRING sKey
[ ] ANYTYPE aValue
|
Теперь вернемся к файлу Lesson02.t. В нем пока содержится только подключение дополнительных файлов. Приступим к написанию кода. Дополняет файл Lesson02.t следующими строками
|
Code
|
[+] main()
[ ] // @ operator usage demonstration
[ ] Pair pPair
[ ] STRING sValue
[ ]
|
Здесь мы объявили переменные, необходимые для нашего примера. Pair pPair - это record, поля которого мы будем заполнять. STRING sValue - это строка, которая будет использоваться в данном примере. Итак, присвоим полю sKey переменной pPair значение "Test Field". Для сопоставления добавим еще строку кода, в которой это поле инициализируется обычным путем, как присваивается значение любому полю record-а. Код имеет вид:
|
Code
|
[ ] // sKey field may be set explicitly
[ ] pPair.sKey = ""
[ ] // sKey field value is set to "Test Field" but sKey field is set as constant string
[ ] pPair.@("sKey") = "Test Field"
[ ]
|
Фактически в вышеприведенном фрагменте выполнены 2 идентичные по сути операции. В обоих случаях было произведено присвоение, только вначале мы в pPair.sKey присвоили пустую строку, а затем уже более конкретное значение. Это нужно, чтобы проверить, что значение в поле pPair.sKey было занесено именно последней конструкцией. Фактически запись вида:
[ ] pPair.@("sKey") = "Test Field"
полностью идентична записи:
[ ] pPair.sKey = "Test Field"
Этим примером было показано как можно получить объект по его названию, если оно задается в виде константной строки. Но куда больший интерес представляет возможность использования переменных с этим оператором, так как это удобно для параметризации некоторых величин. Следующим фрагментом кода мы присвоим полю pPair.aValue значение 0, при этом имя поля будет храниться в переменной ( для этого нам sValue и пригодится). Код имеет вид:
|
Code
|
[ ] // We may put required field name into some variable
[ ] sValue = "aValue"
[ ] // And then we may retreive field from variable value
[ ] pPair.@( sValue ) = 0
[ ]
|
В данном фрагменте выполнена операция аналогичная записи pPair.aValue = 0, но при этом имя хранилось в переменной. Теперь осталось вывести содержимое pPair в отчет, чтобы посмотреть, получилось ли у нас заполнить поля данной записи. Допишем строки:
|
Code
|
[ ] Print( pPair ) // The output will be {Test Field, 0}
[ ]
|
Теперь можно запустить пример ( клавишей F9). Результат имеет вид:
[ ] Script Lesson02.t - Passed
[ ] Machine: (local)
[ ] Started: 04:47:51PM on 21-Feb-2007
[ ] Elapsed: 0:00:00
[ ] Totals: 0 errors, 0 warnings
[ ]
[ ] {Test Field, 0}
|
Таким образом, оператор @ позволяет добраться до объектов по имени, хранящемся в некоторой строке. В качестве объектов могут выступать самые различные величины. Чтобы продемонстрировать окончательно уровень, на котором работает данный оператор, попробуем присвоить значение переменной некоторого простого типа ( например INTEGER ), причем будем обращаться не к самой переменной, а сохраним ее имя в некоторой строке и уже по этой строке с помощью оператора @ мы попытаемся получить доступ к самой переменной. Итак, в файле Lesson02.t допишем строки:
|
Code
|
[ ] INTEGER iData
[ ] sValue = "iData" // Store some variable name in STRING
[ ] @(sValue) = 1 // Applying to some variable by its name stored in the STRING
[ ]
[ ] @("Print")( "Test" ) // Each object can be resolved by its name using @ operator
|
В последнем примере мы присвоили значение 1 переменной iData, при этом мы присваивали не переменной напрямую, а разыменовывали строку, содержащую имя данной переменной.
Принцип работы данного оператора можно объяснить тем, что все объекты, используемые SilkTest-ом, хранятся в некоторой внутренней базе данных. Эта база доступна и во время выполнения. Данный оператор позволяет осуществлять доступ к этой базе. Подобная особенность характерна для скриптовых языков, в частности в PHP есть аналог данного оператора.
Данный оператор представляет собой достаточно мощное средство для динамической работы с объектами, что делает код более гибким и предоставляет большие возможности вплоть до того, что непосредственно код может содержать вообще минимум объектов, а сами объекты считываются откуда-то из внешних ресурсов.
Операторы перехода по условию
Одним из ключевых моментов автоматизированного тестирования является осуществление проверок, а также определение дальнейших действий для выполнения исходя из определенных условий. Иными словами скрипт, написанный на 4Test-е в общем случае имеет различные разветвления - участки кода, выполняющиеся только при определенных условиях. Для реализации подобных переходов используются условные операторы.
Триграф
Простейшим условным оператором является триграф - тернарный оператор ( оператор с 3-мя операндами ) , который имеет вид:
( <Condition> )?( <expr1> ):( <expr2> )
Здесь:
- <Condition> - логическое выражение, которое в результате принимает значение TRUE или FALSE, или может быть приведено к данным величинам, исходя из правил преобразования типов данных;
- <expr1> - выражение, которое будет выполнено, если <Condition> равняется TRUE;
- <expr2> - выражение, которое будет выполнено, если <Condition> равняется FALSE.
Этот оператор используется, если при выполнении условия нужно выполнить одно действие. Также эта конструкция удобна тем, что она может являться частью некоторого выражения, фактически он только и используется как часть выражения. Рассмотрим это на конкретном примере. Допустим, переменная iValue содержит число в диапозоне от 1 до 9, которое генерируется случайным образом. Переменная iData принимает значение -1, если iValue меньше 5 или значение 1 в противном случае. Дописываем в файле Lesson02.t следующие строки, реализующие данное действие:
|
Code
|
[ ] iValue = RandInt( 1 , 9 ) // iValue is randomly generated value in range from 1 to 9
[ ] iData = ( iValue < 5 )?( -1 ):( 1 ) // iData has value -1 if iValue less than 5 and it has value 1 in other cases
|
Теперь усложним немного пример, с целью показать, что данная конструкция может всободно входить в состав некоторого выражения. Допустим, что iValue по-прежнему генерируется в диапозоне от 1 до 9, но при этом если значение данной переменной меньше 5, то переменной iData нужно присвоить значение iValue-1. В противном случае переменной iData присваивается значение iValue+1. Реализуется это так:
|
Code
|
[ ] // Conditional operator being used as part of expression
[ ] iValue = RandInt( 1 , 9 ) // iValue is randomly generated value in range from 1 to 9
[ ] iData = iValue + ( iValue < 5 )?( -1 ):( 1 )
|
Допустимы и вложенные конструкции данного оператора, но, как правило, они представляют серьезную трудность для чтения ( практика показывает, что читать код приходится значительно чаще, чем писать его ), поэтому для таких случаев следует прибегнуть к другим способам реализации данной задачи.
Конструкция if ... else
Идем дальше. Следующий из рассматриваемых операторов является наиболее распростаненным и наиболее общим для данной группы операторов. Это конструкция if ... else. Она позволяет выполнять некоторый блок кода только при выполнении определенного условия. Данная конструкция имеет вид:
if ( <condition> )
<statemet_block_1>
[else
<statemet_block_2>]
Здесь:
- <condition> - логическое выражение, возвращающее TRUE либо FALSE или значение, приводимое к данным величинам по правилам преобразования типов.
- <statemet_block_1> - блок кода, который будет выполняться, если будет выполнено условие <condition>
- <statemet_block_2> - блок кода, который будет выполняться, если условие <condition> не выполняется. Данные блок кода следует за ключевым словом else. Данная часть конструкции является необязательной.
Реализуем пример из предыдущего пункта с помощью данной конструкции. То есть, если iValue меньше 5, то переменной iData нужно присвоить значение iValue-1. В противном случае переменной iData присваивается значение iValue+1. Код имеет вид:
|
Code
|
[ ] // if ... else usage
[ ] iValue = RandInt( 1 , 9 ) // iValue is randomly generated value in range from 1 to 9
[+] if( iValue < 5 )
[ ] iData = iValue - 1
[+] else
[ ] iData = iValue + 1
|
Следует также отметить, что 4Test использует синтаксические особенности различных языков программирования, что позволяет использовать разные стили написания одних и тех же синтаксических конструкций. Так, например, конструкция if ... else использует скобки для помещения в них логического условия, по аналогии с языком С, где при отсутствии скобок будет синтаксическая ошибка. В 4Test-е скобки для данной конструкции необязательные, то есть корректным будет и такая запись if ... else-конструкции:
|
Code
|
[ ] // Alternative if ... else form
[+] if iValue < 5
[ ] iData = iValue - 1
[+] else
[ ] iData = iValue + 1
|
Подобные мелочи позволяют упростить освоение 4Test-а, подстраиваясь под определенные предпочтения по написанию тех или иных конструкций.
Любой блок кода if ... else-конструкций может содержать в себе if ... else-конструкции, то есть вполне допустима вложенность данных конструкций. Усложним наш рассматриваемый пример с переменными iValue и iData, скорректировав условие, что если iValue больше 5, то iData равняется iValue-1 и iData равняется 0, если iValue равно 5. То есть у нас уже есть 3 условия:
- iValue < 5 → iData = iValue - 1
- iValue > 5 → iData = iValue + 1
- iValue == 5 → iData = 0
Реализуется данное действие следующим кодом:
|
Code
|
[ ] // Compound if ... else statement
[+] if( iValue < 5 )
[ ] iData = iValue - 1
[+] else
[+] if( iValue > 5 )
[ ] iData = iValue + 1
[+] else
[ ] iData = 0
|
Вот таким образом записываются сложные if ... else-конструкции. Одним из серьезных недостатков последней записи является ее вложенность, что как минимум неудобно для чтения. Здесь этот недостаток незаметен, так как условий мало, но если их будет где-то более 5-10, то в такой записи код читать уже неудобно. Данную конструкцию можно преобразовать в структуру, вложенность которой не будет увеличиваться при увеличении числа условий. Тот же самый код можно записать в виде:
|
Code
|
[ ] // Linearized compound if ... else statement
[+] if( iValue < 5 )
[ ] iData = iValue - 1
[+] else if( iValue > 5 )
[ ] iData = iValue + 1
[+] else
[ ] iData = 0
|
Данный пример использует расширенный else-оператор, а именно его else if расширение, которое подразумевает наличие условия выполнения дальнейшего блока. Поэтому если возникает необходимость реализации выполнения одного из множества условий, то лучше использовать последнюю конструкцию, как более удобную для чтения.
Конструкция select
В предыдущем примере была показана реализация сложных условных выражений посредством if ... else оператора. Но для случая, когда нужно выполнить некоторые действия при выполнении соответствующего условия из множества возможных, целесообразно использовать другие более пригодные для данной задачи конструкции. Одной из таких конструкций является конструкция select. Синтаксис данной конструкции имеет вид:
select
case <Условие_1>
statements_1
[ case <Условие_2>
statements_2
...
case <Условие_n>
statements_n ]
[ default
statements ]
Здесь,
- <Условие_1> ... <Условие_n> - логические выражения ( возвращающие TRUE или FALSE ), стоящие за ключевым словом case. Если условие истинное, то выполнится нижеследующий блок кода.
- statements_1 ... statements_n - блок кода, который выполнится при выполнении вышестоящего case-условия.
- default - ключевое слово обозначающее блок, который будет выполнен только в том случае, если ни одно из case-условий не выполнено.
Используя данную конструкцию, мы можем реализовать предыдущий пример в виде:
|
Code
|
[ ] // Select statement usage
[+] select
[+] case ( iValue < 5 )
[ ] iData = iValue - 1
[+] case ( iValue > 5 )
[ ] iData = iValue + 1
[+] default
[ ] iData = 0
|
Этот код выполнит те же самые преобразования, что и предыдущий пример. То есть данные конструкции абсолютно идентичны. Выбор конкретной реализации подобных задач остается на усмотрение разработчика скриптов. Тем не менее данная конструкция рекомендуется для реализации сложного ветвления, при котором при соответствующем условии должны выполниться соответствующие действия.
Конструкция switch
Теперь попробуем реализовать множественное ветвление, при этом некоторый блок выполнится если вполне определенная величина принимает некоторое фиксированное значение. Например, запишем выражение, вычисляющее значение iData при некотором значении iValue по следующим правилам:
- iValue = 0 → iData = 3;
- iValue = 1 → iData = 2;
- iValue = 2 → iData = 1;
- iData = 0 в остальных случаях.
С помощью оператора select это реализуется в виде:
|
Code
|
[ ] // Select statement usage for some definite values
[+] select
[+] case ( iValue == 0)
[ ] iData = 3
[+] case ( iValue == 1)
[ ] iData = 2
[+] case ( iValue == 2)
[ ] iData = 1
[+] default
[ ] iData = 0
|
Следует отметить, что в этом выражении все условия сводятся к проверке ОДНОЙ переменной некоторому фиксированному значению. В языке С такое ветвление реализуется с помощью оператора switch. В 4Test-е тоже имеется такой оператор и его структура имеет вид:
switch ( <выражение> )
case <case-value(s)>
statement
[case <case-value(s)>
statement ]...
[ default
statement ]
Его основным отличием является то, что данный оператор работает с конкретными значениями некоторого одного выражения. Предыдущий пример реализуем при помощи оператора switch и получим код:
|
Code
|
[ ] // Switch usage
[+] switch ( iValue )
[+] case 0
[ ] iData = 3
[+] case 1
[ ] iData = 2
[+] case 2
[ ] iData = 1
[+] default
[ ] iData = 0
|
Каждый case данной конструкции может содержать одно или несколько значений, перечисленных через запятую. В качестве демонстрации такой возможности скорректируем предыдущий пример, обработав еще несколько случаев, которые соответствуют случаям по умолчанию:
|
Code
|
[+] switch ( iValue )
[+] case 0
[ ] iData = 3
[+] case 1
[ ] iData = 2
[+] case 2
[ ] iData = 1
[+] case 3, 4
[ ] iData = 0
[+] default
[ ] iData = 0
|
То есть выполняемые действия те же самые, но при этом было показано, что в case можно передавать несколько значений.
Это были перечислены основные виды операторов ветвления. Каждый из них предназначен для определенных случаев и желательно уяснить, где и как используется тот или иной оператор, чтобы потом выбрать оптимальный оператор для решения поставленных задач.
Операторы цикла
Часто возникает необходимость неоднократного выполнения некоторых идентичных задач. Дублирование кода просто увеличивает размер файла, что потом ведет к трудностям при чтении. Поэтому есть необходимость в конструкциях, которые позволяют выполнять повторяющиеся операции. Такими операторами являются операторы цикла.
while
В наиболее общем случае повторять действия нужно до тех пор, пока выполняется некоторое условие или пока условие не выполняется. То есть критерием завершения цикла в любом случае является некоторое условие. В подобное описание наиболее четко укладывается оператор while, который выполняет определенный набор действий до тех пор, пока действительно некоторое условие. Конструкция данного оператора имеет вид:
while ( <condition> )
<statements>
Здесь,
- condition - условие, при котором цикл осуществляет переход на новую итерацию. Это некоторое выражение, результатом которого будет величина типа BOOLEAN или некоторая величина, которая может быть приведена к данному типу по правилам преобразования типов.
- statements - набор выражений, которые будут выполняться циклически, пока действительно условие condition.
В качестве примера реализуем код, который очищает строку. То есть мы инициализируем строку некоторым значением, а затем в цикле будем удалять по символу. Код выглядит примерно так:
|
Code
|
[ ] // While sample
[ ] sValue = "Test String"
[+] while( sValue != "" )
[ ] sValue[1] = ""
|
Это только пример, так как то же самое мы могли бы сделать обычным присвоением пустой строки, но в джанном случае показано, как можно использовать опрератор while. Итак, условием, по которому цикл выполняется, является неравенство sValue пустой строке. Таким образом, пока строка не пустая мы удалаем первый символ строки.
for
Достаточно часто возникает необходимость выполнить некоторые действия фиксированное количество раз. Или просто фиксировать количество пройденных итераций цикла. Например, нам нужно напечатать фразу "Hello World <iteration_number>" некоторое количество раз, которое задается в переменной iValue. Здесь <iteration_number> - это номер итерации цикла. При помощи while это делается так:
|
Code
|
[ ] // While sample with finite itarations count
[ ] iValue = 5
[+] while( iValue > 0 )
[ ] Print ( "Hello World { 5 - iValue }" )
[ ] iValue--
|
Предыдущий пример является ненадежным, так как если мы поставим другое начальное значение для iValue, то вывод результатов уже будет некорректным. То есть нужна конструкция, которая включает в себя помимо условия завершения цикла еще и величину, которая фиксирует некоторую переменную, которая меняется в цикле. Для таких задач наиболее оптимальным является оператор for, конструкция которого имеет вид:
for ( <initial_statement> ; <condition> ; <increment_statement> )
<statements>
Здесь,
- <initial_statement> - начальная инициализация цикла. Это некоторое выражение, которое устанавливает начальное значение переменной цикла
- <condition> - условие выхода из цикла
- <increment_statement> - выражение, которое меняет переменную цикла
- <statements> - блок кода, выполняемый в цикле
Таким образом имеется целая конструкция, в которой задается начальное значение некоторой переменной, условие выхода из цикла и действие, которое выполняется при переходе от одной итерации к следующей. Используя данную конструкцию реализуем предыдущий пример в виде:
|
Code
|
[ ] // For statement usage
[ ] INTEGER i
[ ] iValue = 5
[+] for ( i = 1 ; i <= iValue ; i++ )
[ ] Print( "Hello World {i}" )
|
В данном примере переменная i циклически меняется от 1 до 5. Как работает данный цикл. Вначале срабатывает i = 1, после чего проверяется условие i <= iValue. Далее выполняются действия внутри цикла. Когда действия цикла выполнились, то срабатывает i++, потом проверяется условие i <= iValue и если оно еще выполняется, то цикл заходит на следующую итерацию.
Некоторые элементы цикла for могут быть пропущены, что делает данный вид цикла достаточно универсальным. Главное, чтобы символы ; выделяли нужные операнды. Например, если оставить только операнд, отвечающий за проверку условия выполнения цикла, то данная конструкция будет ана
