Difference between revisions of "Function/ru"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed syntax highlighting)
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
{{Function}}
 
{{Function}}
== Обзор ==
 
[[Keyword/ru|Ключевое слово]] '''function''' предназначено для объявления [[Routine/ru|подпрограммы]], которая может быть вызвана
 
* из [[Unit/ru|модуля]], в котором она объявлена
 
* из внешнего модуля, если она объявлена в секции [[Interface/ru|interface]] модуля,
 
* или из [[Program/ru|программы]]
 
  
Если подпрограмма объявлена как функция, то она возвращает значение. Подпрограмма, не возвращающая значение, является ''[[Procedure/ru|процедурой]]''.
+
__TOC__
  
Функция, являющаяся частью объекта, называется [[Property/ru|свойством]] и с её помощью можно присваивать/возвращать значение (если функция является [[Method/ru|методом]], то вы не сможете присвоить значение)
+
'''Функция''' — это [[Routine|подпрограмма]], которая, в отличие от [[Procedure|процедур]], возвращает значение. Вызов функции практически заменяется возвращаемым значением. Если состояние переключателя компилятора [[$extendedSyntax|<syntaxhighlight lang="pascal" inline>{$extendedSyntax}</syntaxhighlight>]] отключено, вызовы функций не могут отображаться как непродуктивные операторы, но должны быть целиком или частью [[expression|выражения]].
  
== Параметры функции ==
+
Слово <syntaxhighlight lang="pascal" inline>function</syntaxhighlight> является [[Reserved word|зарезервированным словом]].
  
* Передаваемые по значению
+
== Возвращаемое значение ==
* [[Variable parameter/ru|Параметры-переменные]] (передаваемые по ссылке)
 
* Выходные параметры ('''Out''')
 
* Константные параметры
 
* [[Default parameter/ru|Параметры по умолчанию]]
 
* Открытый массив
 
* Массив констант
 
  
== Пример ==
+
В дополнение к обычной процедуре формальная сигнатура функции содержит [[Data_type/ru|тип]] возвращаемого значения: список формальных параметров должен сопровождаться [[Colon|двоеточием]] и типом возвращаемого значения. Например, следующая функция возвращает  [[Boolean|<syntaxhighlight lang="pascal" inline>Boolean</syntaxhighlight>]]
Пример сложения двух [[Integer/ru|целочисленных]] значений:
 
  
<syntaxhighlight lang=pascal>
+
<syntaxhighlight lang="pascal">
function add(c1, c2 : integer) : integer;
+
function myFunction(const firstParameter: real): Boolean;
begin
+
</syntaxhighlight>
add := c1 + c2; //или используйте result := в режиме Object Pascal/Delphi
+
 
end;
+
При реализации функций есть несколько способов определить возвращаемое значение функции.
 +
 
 +
<syntaxhighlight lang="pascal" line highlight="10">
 +
program functionDemo(input, output, stderr);
  
var
+
{$mode objFPC}
  total: integer;
 
  
begin
+
// традиционный синтаксис:
  total := add(4, 5);
+
// результат сохраняется в переменной
  writeln (total); // результатом будет 9
+
// его имя такое же, как у функции
end.
+
function myLine(const x: real): real;
 +
begin
 +
myLine := 0.5 * x + 2;
 +
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
Если <syntaxhighlight lang="pascal" inline>{$modeswitch result+}</syntaxhighlight>, заданный [[Mode ObjFPC|<syntaxhighlight lang="pascal" inline>{$mode objFPC}</syntaxhighlight>]] и [[Mode Delphi|<syntaxhighlight lang="pascal" inline>{$mode Delphi}</syntaxhighlight>]], внутри блока реализации также доступен результат специального идентификатора:
 +
 +
<syntaxhighlight lang="pascal" line start="13" highlight="4">
 +
// используя специальный идентификатор `result`
 +
function myParabola(const x: real): real;
 +
begin
 +
result := sqr(x) - 1;
 +
end;
 +
</syntaxhighlight>
 +
 +
Кроме того, в <syntaxhighlight lang="pascal" inline>{$mode objFPC}</syntaxhighlight> подпрограмма {{Doc|package=RTL|unit=system|identifier=exit|text=<syntaxhighlight lang="pascal" inline>exit</syntaxhighlight>}} также установит возвращаемое значение ''и'' покинет стековый кадр. В предыдущих двух примерах могли появиться дополнительные операторы, и они были бы выполнены, уже после того, как <syntaxhighlight lang="pascal" inline>exit</syntaxhighlight> подпрограмма ''выполнится''. Это поведение оператора <syntaxhighlight lang="C" inline>return</syntaxhighlight> в C или других языках программирования.
 +
 +
<syntaxhighlight lang="pascal" line start="19" highlight="4">
 +
// использование процедуры выхода exit
 +
function even(const x: longint): Boolean;
 +
begin
 +
  exit(not odd(x));
 +
end;
 +
</syntaxhighlight>
 +
 +
В языке [[Assembly language|ассемблера]] применяются другие правила. Если тип возвращаемого значения является целым числом, используется регистр-накопитель, при условии, что он помещается туда:
 +
 +
<syntaxhighlight lang="pascal" line start="25" highlight="9,12,23">
 +
// на ассемблере:
 +
// возвращаемый тип умещается в один регистр => использовать регистр-накопитель
 +
function zero(const x: int64): Boolean;
 +
{$ifDef CPUx86_64}
 +
assembler; register;
 +
{$asmMode intel}
 +
asm
 +
// xor изменяет флаги => поместите его перед тестом
 +
xor rax, rax    // rax := 0    (удалить остаток)
 +
 +
test x, x        // x = 0 ?
 +
setz al          // rax := ZF
 +
 +
        // Когда вы изучаете вывод ассемблера
 +
        // вы заметите, что компилятор автоматически вставляет код
 +
        // который перемещает содержимое rax в нужное место в стеке,
 +
        // пока не появится подсказка noStackFrame
 +
        // (автоматически устанавливается некоторыми уровнями оптимизации)
 +
        // инструктирует компилятор опустить кадр стека, если это возможно.
 +
end;
 +
{$else}
 +
begin
 +
result := x = 0;
 +
end;
 +
{$endIf}
 +
</syntaxhighlight>
 +
 +
В противном случае, в зависимости от того, какой режим [[sAsmmode|<syntaxhighlight lang="pascal" inline>{$asmMode}</syntaxhighlight>]] активен, можно использовать макрос <syntaxhighlight lang="asm" inline>@result</syntaxhighlight> (Intel) или <syntaxhighlight lang="asm" inline>__result</syntaxhighlight> (AT&T).
 +
 +
<syntaxhighlight lang="pascal" line start="51" highlight="20,31,36,40,41">
 +
type
 +
bodyAttributes = record
 +
surfaceArea: real;
 +
volume: real;
 +
end;
 +
 +
// на ассемблере:
 +
// возвращаемый тип не помещается в аккумулятор => макрос @result дает адрес
 +
function sphere(const radius: real): bodyAttributes;
 +
{$ifDef CPUx86_64}
 +
assembler;
 +
{$asmMode intel}
 +
const
 +
three: longint = 3;
 +
four: longint = 4;
 +
var
 +
r: real;
 +
asm
 +
pextrq r, radius, 0 // r := (@radius+0)^
 +
lea rax, @result    // rax := @result
 +
fld r              // radius
 +
 +
fld st(0)          // radius radius
 +
fild four          // 4 radius radius
 +
fldpi              // pi 4 radius radius
 +
fmul                // 4*pi radius radius
 +
fxch                // radius 4*pi radius
 +
fld st(0)          // radius radius 4*pi radius
 +
fmul                // radius^2 4*pi radius
 +
fmul                // 4*pi*radius^2 radius
 +
fst [rax].bodyAttributes.surfaceArea
 +
 +
fmul                // 4*pi*radius^3
 +
fild three          // 3 4*pi*radius^3
 +
fdivp              // 4/3*pi*radius^3
 +
fst [rax].bodyAttributes.volume
 +
end;
 +
{$else}
 +
begin
 +
sphere.surfaceArea := 4 * pi() * sqr(radius);
 +
sphere.volume := 4 / 3 * pi() * sqr(radius) * abs(radius);
 +
end;
 +
{$endIf}
 +
</syntaxhighlight>
 +
 +
Первоначально Паскаль ожидал ''ровно одно'' [[Becomes|присваивание]] переменной результата (в зависимости от того, что используется). Однако [[FPC]] не запрещает множественные присваивания. Он выдаст предупреждение, если не был использован ни один из возможных идентификаторов результата или процедура выхода не написана.
 +
 +
<blockquote>
 +
; Warning: Function result does not seem to be set (''рус''. Предупреждение: результат функции, похоже, не установлен)
 +
: You can get this warning if the [[Compiler|compiler]] thinks that a function return value is not set. This will not be  displayed for assembler procedures, or procedures that contain assembler blocks.(''рус''. Вы можете получить это предупреждение, если [[Compiler|компилятор]]  считает, что возвращаемое значение функции не установлено. Это не будет отображаться для ассемблерных процедур или процедур, содержащих ассемблерные блоки.)
 +
</blockquote>
 +
 +
<syntaxhighlight lang="pascal" line start="95">
 +
begin
 +
writeLn(sphere(2.0).surfaceArea);
 +
end.
 +
</syntaxhighlight>
 +
 +
Обратите внимание, что в случае [[Operator overloading|перегрузок операторов]] в формальной подписи должен быть объявлен специальный вид функции, идентификатор переменной результата. Подробнее см. в статье.
 +
 +
== Замечания ==
 +
 +
[[Pure functions]] это функции, не зависящие от внешнего состояния.
 +
 +
== См.также ==
 +
 +
* [[Pascal basics]]
 +
* [https://www.freepascal.org/docs-html/ref/refch14.html § “Using functions and procedures” in the Reference Guide]
 +
* [https://www.freepascal.org/docs-html/prog/progse10.html § “Intel 80x86 Inline assembler” in the Programmer’s Guide]

Latest revision as of 00:25, 1 June 2022

Deutsch (de) English (en) español (es) suomi (fi) français (fr) русский (ru)

Функция — это подпрограмма, которая, в отличие от процедур, возвращает значение. Вызов функции практически заменяется возвращаемым значением. Если состояние переключателя компилятора {$extendedSyntax} отключено, вызовы функций не могут отображаться как непродуктивные операторы, но должны быть целиком или частью выражения.

Слово function является зарезервированным словом.

Возвращаемое значение

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

function myFunction(const firstParameter: real): Boolean;

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

 1program functionDemo(input, output, stderr);
 2
 3{$mode objFPC}
 4
 5// традиционный синтаксис:
 6// результат сохраняется в переменной
 7// его имя такое же, как у функции
 8function myLine(const x: real): real;
 9begin
10	myLine := 0.5 * x + 2;
11end;

Если {$modeswitch result+}, заданный {$mode objFPC} и {$mode Delphi}, внутри блока реализации также доступен результат специального идентификатора:

13// используя специальный идентификатор `result`
14function myParabola(const x: real): real;
15begin
16	result := sqr(x) - 1;
17end;

Кроме того, в {$mode objFPC} подпрограмма exit также установит возвращаемое значение и покинет стековый кадр. В предыдущих двух примерах могли появиться дополнительные операторы, и они были бы выполнены, уже после того, как exit подпрограмма выполнится. Это поведение оператора return в C или других языках программирования.

19// использование процедуры выхода exit
20function even(const x: longint): Boolean;
21begin
22  exit(not odd(x));
23end;

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

25// на ассемблере:
26// возвращаемый тип умещается в один регистр => использовать регистр-накопитель
27function zero(const x: int64): Boolean;
28{$ifDef CPUx86_64}
29assembler; register;
30{$asmMode intel}
31asm
32	// xor изменяет флаги => поместите его перед тестом
33	xor rax, rax     // rax := 0    (удалить остаток)
34	
35	test x, x        // x = 0 ?
36	setz al          // rax := ZF
37	
38        // Когда вы изучаете вывод ассемблера
39        // вы заметите, что компилятор автоматически вставляет код
40        // который перемещает содержимое rax в нужное место в стеке,
41        // пока не появится подсказка noStackFrame
42        // (автоматически устанавливается некоторыми уровнями оптимизации)
43        // инструктирует компилятор опустить кадр стека, если это возможно.
44end;
45{$else}
46begin
47	result := x = 0;
48end;
49{$endIf}

В противном случае, в зависимости от того, какой режим {$asmMode} активен, можно использовать макрос @result (Intel) или __result (AT&T).

51type
52	bodyAttributes = record
53		surfaceArea: real;
54		volume: real;
55	end;
56
57// на ассемблере:
58// возвращаемый тип не помещается в аккумулятор => макрос @result дает адрес
59function sphere(const radius: real): bodyAttributes;
60{$ifDef CPUx86_64}
61assembler;
62{$asmMode intel}
63const
64	three: longint = 3;
65	four: longint = 4;
66var
67	r: real;
68asm
69	pextrq r, radius, 0 // r := (@radius+0)^
70	lea rax, @result    // rax := @result
71	fld r               // radius
72	
73	fld st(0)           // radius radius
74	fild four           // 4 radius radius
75	fldpi               // pi 4 radius radius
76	fmul                // 4*pi radius radius
77	fxch                // radius 4*pi radius
78	fld st(0)           // radius radius 4*pi radius
79	fmul                // radius^2 4*pi radius
80	fmul                // 4*pi*radius^2 radius
81	fst [rax].bodyAttributes.surfaceArea
82	
83	fmul                // 4*pi*radius^3
84	fild three          // 3 4*pi*radius^3
85	fdivp               // 4/3*pi*radius^3
86	fst [rax].bodyAttributes.volume
87end;
88{$else}
89begin
90	sphere.surfaceArea := 4 * pi() * sqr(radius);
91	sphere.volume := 4 / 3 * pi() * sqr(radius) * abs(radius);
92end;
93{$endIf}

Первоначально Паскаль ожидал ровно одно присваивание переменной результата (в зависимости от того, что используется). Однако FPC не запрещает множественные присваивания. Он выдаст предупреждение, если не был использован ни один из возможных идентификаторов результата или процедура выхода не написана.

Warning
Function result does not seem to be set (рус. Предупреждение: результат функции, похоже, не установлен)
You can get this warning if the compiler thinks that a function return value is not set. This will not be displayed for assembler procedures, or procedures that contain assembler blocks.(рус. Вы можете получить это предупреждение, если компилятор считает, что возвращаемое значение функции не установлено. Это не будет отображаться для ассемблерных процедур или процедур, содержащих ассемблерные блоки.)
95begin
96	writeLn(sphere(2.0).surfaceArea);
97end.

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

Замечания

Pure functions это функции, не зависящие от внешнего состояния.

См.также