6

Процедуры и подпрограммы

 

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

Для чего нужны процедуры или подпрограммы?

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

Подпрограммы

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

Label:

Эта подпрограмма может быть вызвана из любой точки в вашем коде, используя следующую команду:

Gosub Label

Обратите внимание, что после команды Gosub идет название метки подпрограммы на которую следует прыгнуть. После того, как будет отработан код подпрограммы, вернуться назад в основной код программы поможет команда Return:

Return

Ну и конечно пример использования:

a.l = 10
Debug a
Gosub Calculation
Debug a
End


Calculation:
   a = a * 5 + 5
Return

В этом примере, мы присваиваем переменной величину 10 и выводим в отладочное окно. Далее с помощью команды Gosub, мы заставляем программу сделать прыжок на подпрограммную метку Calculation: . В подпрограмме производятся вычисления с переменной a, в результате которых в переменной окажется число 55. Командой же RETURN делается возврат из подпрограммы в основной код. И программа продолжит выполнение кода сразу после вызова Gosub Calculation. То есть с инструкции Debug a. Которая выведет результат 55 в отладочное окно. Следующая инструкция END завершит выполнение программы.

Примечание относительно позиции подпрограмм в коде

Хотелось бы отметить одну важную вещь: вызовы подпрограмм могут находится где вам необходимо, но само тело подпрограммы должно находится после команды END, иначе  ваша программа может работать неправильно.

Прыжки из подпрограмм

Хоть подпрограммы считаются плохой практикой кодирования, но раз уж начали описывать их, то я опишу их в полном объеме. Каждая подпрограмма должна содержать команду Return, для перехода обратно в основной код, но в некоторых случаях, при каких нибудь там условиях, нужно выскочить из подпрограммы досрочно. Это достигается с помощью команд FakeReturn и GOTO. Вот пример:

a.l = 10
Debug a
Gosub Calculation
Debug a
End


RunAgain:
a.l = 20
Debug a
Gosub Calculation
Debug a
End


Calculation:
If a = 10
FakeReturn
Goto RunAgain
Else
a = a * 5 + 5
EndIf
Return

Как вы можете видеть из этого небольшого примера, скачки из подпрограмм в зависимости от условий, делают код уродливым. Этот пример не труден для понимания. Переменной а присваивается значение 10. Далее идет вызов подпрограммы Calculation. В ней проверяется условие равенства "a=10", и поскольку условие выполняется, выход из подпрограммы осуществляется досрочно с помощью FakeReturn и GOTO RunAgain, в результате программа начнет выполнение кода с метки RunAgain. В ней переменной присваивавется значение 20, выводится в окно отладки и производится повторный вызов подпрограммы Calculation Как вы наверно поняли теперь условие равно False и программа делает вычисление с переменной. В результате a=105 и с помощью Return производится возврат из подпрограммы. Далее отображение значения в окне отладки и завершение программы. Надеюсь, что эти примеры дали вам понимание применения подпрограмм, но так же я надеюсь, что вы не будете злоупотреблять этой практикой. Вы можете  улучшить структуру кода, используя процедуры.

Основы процедур

Структурное программирование является термином, который ясно определяет PureBasic, который может быть описан как 'Процедурный' и 'Структурный' язык программирования.
Его архитектура просто насыщена процедурами. Процедуры (иногда вызываемые 'Функции' на других языках) являются самой важной частью функциональных возможностей, которые обеспечивает PureBasic. Процедуры по сути только держатель части кода, который можно выполнить в любой момент. Они чем то похожи на подпрограммы. Их можно вызывать сколько угодно раз так же, как и подпрограммы, но в отличие от них процедурам можно задавать множество стартовых параметров, а так же возвратить значение любого типа. Давайте начнем с простого примера процедуры:

Procedure NurseryRhyme()
  Debug "Mary had a little lamb, its fleece was white as snow."
  Debug "And everywhere that Mary went, that lamb was sure to go."
EndProcedure


NurseryRhyme()


Для того чтобы использовать процедуру в коде, вы должны ее сначала определить. Здесь я определил процедуру под названием NurseryRhyme(), используя ключевое слово Procedure. Конец процедуры определяется с помощью ключевого слова EndProcedure. Мы можем вызвать кусок кода, содержащийся в рамках этой процедуры в любое время, просто используя ее имя, например:

NurseryRhyme()

Прежде чем мы продолжим, я должен отметить несколько моментов в этом примере, которые требуют дальнейшего разъяснения. Во-первых, вы наверно заметили, что есть скобки после имени процедуры. Они нужны для использования каких-либо параметров, которые должны быть переданы процедуре. Даже если вы не используете никаких параметров, как в этом примере, все равно необходимо использовать скобки. Кроме того, при вызове процедуры, так же используются скобки как часть вызова процедуры с параметрами или без. Кроме того хотелось бы отметить, что имя процедуры, например, NurseryRhyme() не должно содержать пробелов и необходимо придерживаться тех же принципов именования, изложенных для переменных. Если вы запустите приведенный выше пример, вы увидите в окне вывода отладки детский стишок, который содержится в теле вызванной процедуры NurseryRhyme(). Вы можете сколько угодно выполнять код этой процедуры, для этого достаточно просто вызывать ее снова и снова, используя ее имя.

Procedure NurseryRhyme()
  Debug "Mary had a little lamb, its fleece was white as snow."
  Debug "And everywhere that Mary went, that lamb was sure to go."
EndProcedure


For x.l = 1 To 5
  NurseryRhyme()
Next x

Процедуры можно вызвать из любого места программы, как и в приведенном выше примере, где я вызвал несколько раз процедуру NurseryRhyme() из цикла. Процедура может быть вызвана так же из других процедур:

Procedure NurseryRhyme2()
  Debug "And everywhere that Mary went, that lamb was sure to go."
EndProcedure


Procedure NurseryRhyme1()
  Debug "Mary had a little lamb, its fleece was white as snow."
  NurseryRhyme2()
EndProcedure


NurseryRhyme1()

В приведенном выше примере видно, как сначала вызывается процедура NurseryRhyme1(), а из нее уже идет вызов процедуры NurseryRhyme2()

Примечание относительно позиции процедур в коде

Приведенный выше пример четко показывает позиции процедур. Вы наверно заметили, что первой в коде объявлена процедура NurseryRhyme2() а уже потом процедура NurseryRhyme1(). Это сделано намеренно и было необходимо в силу того, что вы всегда должны объявлять процедуру перед ее вызовом. PureBasic компилятор считывает исходный код сверху вниз, и по этой причине компиляция вызовет ошибку, если вызов процедуры будет до ее объявления. Этот простой пример показывает также, почему почти всегда процедуры определяются в верхней части исходного кода. Как и в обычной жизни, есть исключения и из этого правила. Для того чтобы иметь возможность вызывать процедуру прежде ее объявления, надо использовать ключевое слово Declare. На самом деле Declare не объявляет процедуры, оно просто позволяют компилятору знать, какие процедуры будут вызываться. А теперь пример:

Declare NurseryRhyme1()
Declare NurseryRhyme2()


NurseryRhyme1()


Procedure NurseryRhyme1()
   Debug "Mary had a little lamb, its fleece was white as snow."
   NurseryRhyme2()
EndProcedure

Procedure NurseryRhyme2()
   Debug "And everywhere that Mary went, that lamb was sure to go."
EndProcedure

При использовании Declare определяется только первая строка процедуры, примерно так же, как  ключевое слово Procedure. Далее вы вызываете процедуру в любом месте, но ниже Declare. Ну и не забываем реально объявлять процедуру в каком нужно месте, поскольку Declare как бы симулирует объявление процедуры.

Программная область

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

a.l = 10


Procedure DisplayValue()
  Debug a
EndProcedure


DisplayValue()

Здесь я определил переменную с типом Long, и присвоил ей значение ‘10’. Так же я определил процедуру ‘DisplayValue()’ и в этой процедуре всего одна строка программы: отобразить в окне отладки число присвоенное переменной. Последняя строка этого примера вызывает процедуру. Теперь, если вы выполните этот пример, то наверно ожидаете, что в отладочном окне должно быть отображено 10, но вместо этого вы видите 0. Позвольте мне все же объяснить в чем дело. Переменные в процедурах являются локальными, то есть доступные только в теле процедуры. А это значит, что переменная а которой присвоено значение 10 и переменная а находящаяся в процедуре это две разные переменные. Для того же чтобы переменная а  стала общей или единой для основного кода и процедуры ее необходимо сделать глобальной. То есть вот так:

Global a.l = 10


Procedure DisplayValue()
  Debug a
EndProcedure


DisplayValue()

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

Procedure DisplayValue()
  a.l = 10
EndProcedure


DisplayValue()


Debug a

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

Procedure DisplayValue()
   Global a.l = 10
EndProcedure


DisplayValue()


Debug a


Теперь в окне отладки будет 10.

Читая эти последние несколько примеров вы можете подумать, почему бы не сделать все переменные  глобальными? Когда программы достигают больших масштабов, код может стать запутанным, если все переменные являются глобальными. Использование различных областей в рамках вашей программы также позволяет использовать временные имена переменных в рамках процедуры для расчетов или циклов, и вы можете быть уверены, что они не повлияют на любые переменные извне. Некоторые программисты стремятся использовать как можно меньше глобальных переменных, поскольку это делает отладку программы не такой сложной. При использовании массивов и связанных списков с процедурами, они тоже могут иметь разные диапазоны в рамках вашей программы так же, как переменные. До PureBasic V4, все массивы и связанные списки являлись глобальными по умолчанию. С приходом PureBasic V4, массивы и связанные списки могут быть локальными и глобальными, то есть такие же как и переменные. Далее я опишу все более расширенно, с большим количеством примеров, чтобы продемонстрировать использование переменных, массивов и связанных списков в различных рамках программы.

Команда GLOBAL

Глобальные переменные

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

Global a.l = 10


Procedure DisplayValue()
  Debug a
EndProcedure


DisplayValue()

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

Глобальные массивы

Global Dim Numbers.l(1)


Procedure ChangeValues()
  Numbers(0) = 3
  Numbers(1) = 4
EndProcedure


ChangeValues()
Debug Numbers(0)
Debug Numbers(1)

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

Глобальная Связные списки

Global NewList Numbers.l()
AddElement(Numbers())
Numbers() = 1

Procedure ChangeValue()
  SelectElement(Numbers(), 0)
  Numbers() = 100
EndProcedure


ChangeValue()
SelectElement(Numbers(), 0)
Debug Numbers()

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

Команда  Protected

Protected(местные) переменные

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

 Global a.l = 10

Procedure ChangeValue()
   Protected a.l = 20
EndProcedure

ChangeValue()
Debug a

Вы можете видеть, что даже если местная переменная внутри процедуры имеет такое же имя, что и глобальная переменная, она считается другой переменной или переменной в другой области. Если вы запустите выше пример, то увидите результат 10 в окне отладки, хоть мы и вызываем процедуру ChangeValue() в которой, мы вроде как присваиваем переменной с таким же именем число 20. Поскольку перед переменной стоит команда Protected,  процедура ни как не влияет на переменную в основном коде.

Protected(местные) массивы

Global Dim Numbers.l(1)

Procedure ChangeValues()
   Protected Dim Numbers.l(1)
   Numbers(0) = 3
   Numbers(1) = 4
EndProcedure

ChangeValues()
Debug Numbers(0)
Debug Numbers(1)

В этом примере мы используем команду Protected  точно так же, как c защищенными переменными. Если вы запустите пример выше, результаты в окне вывода отладки будут нули хоть мы и вызвали процедуру ChangeValues(), в которой меняются данные в ячейках массива. Но как вы поняли это ни как не влияет на глобальный массив в основном коде.

Protected(местные) связанные списки

Global NewList Numbers.l()
AddElement(Numbers())
Numbers() = 1

Procedure ChangeValue()
   Protected NewList Numbers.l()
   AddElement(Numbers())
   Numbers() = 100
EndProcedure

ChangeValue()
SelectElement(Numbers(), 0)
Debug Numbers()

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

Команда Shared

Shared (общие) переменные

Иногда в вашем коде вам может понадобиться доступ к переменным внутри процедуры, которая не была определена как глобальная. Для этого используется команда Shared. Вот пример:

a.l = 10


Procedure ChangeValue()
  Shared a
  a = 50
EndProcedure


ChangeValue()
Debug a

Здесь, несмотря на то что переменная изначально не была определена как глобальная, процедура получила доступ к ней с помощью команды Shared. При запуске приведенного выше примера, процедура изменяет значение переменной, как будто она объявлена глобальной.

Shared (общие) массивы

Dim Numbers.l(1)

Procedure ChangeValues()
    Shared Numbers()
    Numbers(0) = 3
    Numbers(1) = 4
EndProcedure

ChangeValues()
Debug Numbers(0)
Debug Numbers(1)

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

Shared (общие) связанные списки

NewList Numbers.l()

Procedure ChangeValue()
   Shared Numbers()
   AddElement(Numbers())
   Numbers() = 100
EndProcedure

ChangeValue()
SelectElement(Numbers(), 0)
Debug Numbers()

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

Команда Static

Статические(постоянные) переменные

Каждый раз, после выхода из процедуры, будут потеряны все значения переменных, определенных в этой процедуре. Если Вы хотите, чтобы процедура помнила значения своих  переменных после выхода, то вам нужно использовать команду Static перед этими переменными. См. пример:

Procedure ChangeValue()
  Static a.l
  a + 1
  Debug a
EndProcedure

For x.l = 1 To 5
  ChangeValue()
Next x

 

Здесь, в процедуре ChangeValue() я установил статическую переменную, используя команду Static. После этого я увеличил значения этой переменной на 1 и выводил в окно отладки.  Я вызвал эту процедуру пять раз с помощью стандартного цикла. Если вы посмотрите на значения, они все разные, и увеличивается на 1 каждый раз. Это происходит потому, что значение переменной процедура запоминает при выходе из нее.

Статические (постоянные) массивы

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

Procedure ChangeValue()
  Static Dim Numbers.l(1)
  Numbers(0) + 1
  Numbers(1) + 1
  Debug Numbers(0)
  Debug Numbers(1)
EndProcedure

For x.l = 1 To 5
  ChangeValue()
Next x

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

Статические(постоянные) связанные списки

Procedure ChangeValue()
   Static NewList Numbers.l()
    If CountList(Numbers()) = 0
      AddElement(Numbers())
    EndIf
   SelectElement(Numbers(), 0)
   Numbers() + 1
   Debug Numbers()
   EndProcedure

For x.l = 1 To 5
   ChangeValue()
Next x

 

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

Передача переменных процедуре

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

Procedure AddTogether(a.l, b.l)
   Debug a + b
EndProcedure

AddTogether(10, 5)
AddTogether(7, 7)
AddTogether(50, 50)

Все параметры, которые должны быть переданы процедуре должны быть в скобках. И если нужно передать несколько параметров необходимо разделять их  запятыми. Когда определяются параметры , как в прошлом примере, им нужно определять тип. Здесь, я определил два параметра:  A и  B , оба из которых имеют тип Long. При вызове процедуры, определенной ранее с параметрами, нужно не забывать про них:

AddTogether(10, 5)

После этого вызова , значение 10 передается в переменную А,  значение 5 передается в переменную B. Затем процедура складывает эти переменные вместе и отображает результат в окне вывода отладки.Вот еще один пример, но с использованием строк:

Procedure JoinString(a.s, b.s)
  Debug a + b
EndProcedure


JoinString("Mary had a little lamb, ", "its fleece was white as snow.")
JoinString("And everywhere that Mary went, ", "that lamb was sure to go.")
JoinString("..", "..")

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

Procedure LotsOfTypes(a.b, b.w, c.l, d.f, e.s)
   Debug "Byte: " + Str(a)
   Debug "Word: " + Str(b)
   Debug "Long: " + Str(c)
   Debug "Float: " + StrF(d)
   Debug "String: " + e
EndProcedure


LotsOfTypes(104, 21674, 97987897, 3.141590, "Mary had a little lamb")

 

Здесь я использовал несколько различных типов переменных в качестве параметров в мою процедуру LotsOfTypes(), чтобы продемонстрировать, что параметры не все должны быть одного типа. Значения, которые передаются при вызове процедуры назначаются в установленном порядке соответственно. Вы также заметили, что все значения совпадают с  соответствующими им типами параметров, в противном случае будут синтаксические ошибки. Вы также, вероятно, заметили, я использовал две встроенных функций, которые вам могут быть не знакомы. Эти две функции Str() и StrF() будут подробно описаны в главе 7 (примеры общих команд), но все же объясню проще, они превращают числовые типы в строковые.

Правила передачи значений процедурам

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

AddTogether(a.l, b.l)

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

Даже из этих простых примеров видно большая полезность процедур. Они не только экономят время с вводом кода, но они позволяют программистам расширять функциональность PureBasic.

Дополнительные параметры переменных

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

Procedure DisplayParameters(a.l, b.l, c.l = 3)
  Debug a
  Debug b
  Debug c
EndProcedure

DisplayParameters(1, 2)

Если вы посмотрите на определение процедуры в приведенном выше примере, вы увидите, что последний параметр был определен иначе, чем другие. Если последний параметр не указан в вызове процедуры, он должен быть указан по умолчанию. Я вызываю процедуру DisplayParameters(1, 2)  без последнего параметра, но вы заметили, что в окне отладки отобразилось три параметра, поскольку один из параметров был указан по умолчанию. Вы должны понимать, что все дополнительные параметры, указанные по умолчанию, должны всегда определяться справа. Это происходит потому, что значения в процедуру передаются слева направо.

 Передача массивов процедуре.

Вы также можете передать массивы и связанные списки процедурам в качестве параметров. Их использование является  простым. Чтобы передать массив в качестве параметра необходимо сначала определить массив, используя команду Dim. Вот простой одномерный массив строк определенный с четырьмя индексами (помните, что индексы начинаются с  0) я назвал его Countries

Dim Countries.s(3)
  Countries(0) = "England"
  Countries(1) = "Northern Ireland"
  Countries(2) = "Scotland"
  Countries(3) = "Wales"

После того как массив был определен, давайте определим процедуру:

Procedure EchoArray(Array  MyArray.s(1))
  For x.l = 0 To 3
   Debug MyArray(x)
  Next x
EndProcedure

Чтобы определить процедуру с использованием массива в качестве параметра,  вам надо определить массив со скобками, в которых указывается размерность массива(в данном случае у нас одномерный), поэтому в скобках стоит 1. Кроме того необходимо перед именем массива использовать ключевое слово Array.   Далее после имени нужно указывать суффикс ожидаемого типа массива. Параметр-массив выглядит следующим образом:

Array MyArray.s(1)

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

EchoArray(Countries())

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

Dim Countries.s(3)
Countries(0) = "England"
Countries(1) = "Northern Ireland"
Countries(2) = "Scotland"
Countries(3) = "Wales"

Procedure EchoArray(Array MyArray.s(1))
For x.l = 0 To 3
Debug MyArray(x)
Next x
EndProcedure


EchoArray(Countries())

В окне отладки должны отобразится четыре страны

Передача многомерных массивов

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

Dim Countries.s(3, 1)
  Countries(0,0) = "England"
  Countries(0,1) = "57,000,000"
  Countries(1, 0) = "Northern Ireland"
  Countries(1,1) = "2,000,000"
  Countries(2, 0) = "Scotland"
  Countries(2,1) = "5,200,000"
  Countries(3, 0) = "Wales"
  Countries(3,1) = "3,100,000"

Procedure EchoArray(Array MyArray.s(2))
  For x.l = 0 To 3
    Debug MyArray(x,0) + " - " + "Population: " + MyArray(x,1)
  Next x
EndProcedure


EchoArray(Countries())

Хотя это двумерный массив, мы по-прежнему используем те же правила, как и для одномерного, с небольшой разницей: в параметре-массиве, передаваемом процедуре используется цифра 2, указывающая на то, что будет передаваться двумерный массив в качестве параметра:

Array MyArray.s(2)

Далее вызываем процедуру передавая массив как и прежде, без каких-либо типов, индексов или размеров:

EchoArray(Countries())

Передача массивов структурированных типов

Структурированные массивы так же могут быть переданы процедуре в качестве параметра. Все делается так же как и с простыми массивами, меняется лишь тип массива в соответствии со структурой. Посмотрите на следующий пример:

Structure COUNTRY
  Name.s
  Population.s
EndStructure

Dim Countries.COUNTRY(3)
 Countries(0)\Name = "England"
 Countries(0)\Population = "57,000,000"
 Countries(1)\Name = "Northern Ireland"
 Countries(1)\Population = "2,000,000"
 Countries(2)\Name = "Scotland"
 Countries(2)\Population = "5,200,000"
 Countries(3)\Name = "Wales"
 Countries(3)\Population = "3,100,000"

Procedure EchoArray(Array MyArray.COUNTRY(1))
  For x.l = 0 To 3
    Debug MyArray(x)\Name + " - " + "Population: " + MyArray(x)\Population
  Next x
EndProcedure

EchoArray(Countries())

Здесь я использовал структурированный массив в качестве параметра:  Array MyArray.COUNTRY(1)

Передача Связанных списков процедуре

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

NewList Numbers.l()
  AddElement(Numbers())
  Numbers() = 25
  AddElement(Numbers())
  Numbers() = 50
  AddElement(Numbers())
  Numbers() = 75

Procedure EchoList(List MyList.l())
  ForEach MyList()
    Debug MyList()
  Next
EndProcedure

EchoList(Numbers())

Здесь я создал стандартный связанный список, называемый Number.l(), c типом Long. После добавления трех элементов в этот список, я определил процедуру Echolist() со связанным списком в качестве параметра. Как и с массивами, параметр - связанный список состоит  из имени параметра, за которым следует тип и, наконец, набор скобок. Связанные списки не используют индексы, как массивы, так что вам не нужно вводить номер в скобках. Так же необходимо перед именем в связанном списке использовать ключевое слово List .  Когда все это сделано, можно вызвать процедуру, передав связанный список как массив, без какого-либо типа и т.д. :

EchoList(Numbers())

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

Передача структурированных связанных списков

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

 

Structure FLAGS
   Country.s
   Flag.s
EndStructure

NewList Info.FLAGS()
   AddElement(Info())
   Info()\Country = "Great Britain"
   Info()\Flag = "Union Jack"
   AddElement(Info())
   Info()\Country = "USA"
   Info()\Flag = "Stars And Stripes"
   AddElement(Info())
   Info()\Country = "France"
   Info()\Flag = "Tricolore"

Procedure EchoList(List MyList.FLAGS())
   ForEach MyList()
     Debug MyList()\Country + "'s flag is the " + MyList()\Flag
   Next
EndProcedure

EchoList(Info())

И сам параметр из кода:   List MyList.FLAGS()

Возврат значения из процедур

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

Procedure.l AddTogether(a.l, b.l)
  ProcedureReturn a + b
EndProcedure

Debug AddTogether(7, 5)

После слова Procedure следует суффикс того типа, который следует возвратить из процедуры. Для возврата значения используется команда ProcedureReturn . Если вы хотите использовать для возврата другой тип данных, то это можно сделать примерно так:

Procedure.s JoinString(a.s, b.s)
   ProcedureReturn a + b
EndProcedure

Debug JoinString("Red ", "Lorry")
Debug JoinString("Yellow ", "Lorry")

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

Procedure.l AddTogether(a.l, b.l)
   ProcedureReturn a + b
EndProcedure

Debug AddTogether(AddTogether(2, 3), AddTogether(4, 1))

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

Параметры не влияют на тип возврата

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

Procedure.s DisplayValue(a.l, b.s)
   ProcedureReturn Str(a) + b
EndProcedure

x = 5
While x >= 0
  Debug DisplayValue(x, " green bottles hanging on the wall.")
  x - 1
Wend

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

Ограничения связанные с возвратом результатов

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

 
 Содержание                  Учебник                Главная
Сайт создан в системе uCoz