Function/ru: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Function/ru
| Old | New | Line | |
|---|---|---|---|
| 1 | + | **Функция** — это [[Routine|подпрограмма]], которая, в отличие от [[Procedure|процедур]], возвращает значение. Вызов функции практически заменяется возвращаемым значением. Если состояние переключателя компилятора [[$extendedSyntax|{$extendedSyntax}]] отключено, вызовы функций не могут отображаться как непродуктивные операторы, но должны быть целиком или частью [[expression|выражения]]. | |
| 2 | + | ||
| 3 | + | Слово `function` является [[Reserved word|зарезервированным словом]]. | |
| 4 | + | ||
| 5 | + | ## Возвращаемое значение | |
| 6 | + | ||
| 7 | + | В дополнение к обычной процедуре формальная сигнатура функции содержит [[Data type/ru|тип]] возвращаемого значения: список формальных параметров должен сопровождаться [[Colon|двоеточием]] и типом возвращаемого значения. Например, следующая функция возвращает [[Boolean]] | |
| 8 | + | ||
| 9 | + | ``` pascal | |
| 10 | + | function myFunction(const firstParameter: real): Boolean; | |
| 11 | + | ``` | |
| 12 | + | ||
| 13 | + | При реализации функций есть несколько способов определить возвращаемое значение функции. | |
| 14 | + | ||
| 15 | + | ``` pascal | |
| 16 | + | program functionDemo(input, output, stderr); | |
| 17 | + | ||
| 18 | + | {$mode objFPC} | |
| 19 | + | ||
| 20 | + | // традиционный синтаксис: | |
| 21 | + | // результат сохраняется в переменной | |
| 22 | + | // его имя такое же, как у функции | |
| 23 | + | function myLine(const x: real): real; | |
| 24 | + | begin | |
| 25 | + | myLine := 0.5 * x + 2; | |
| 26 | + | end; | |
| 27 | + | ``` | |
| 28 | + | ||
| 29 | + | Если `{$modeswitch result+}`, заданный [[Mode ObjFPC|{$mode objFPC}]] и [[Mode Delphi|{$mode Delphi}]], внутри блока реализации также доступен результат специального идентификатора: | |
| 30 | + | ||
| 31 | + | ``` pascal | |
| 32 | + | // используя специальный идентификатор `result` | |
| 33 | + | function myParabola(const x: real): real; | |
| 34 | + | begin | |
| 35 | + | result := sqr(x) - 1; | |
| 36 | + | end; | |
| 37 | + | ``` | |
| 38 | + | ||
| 39 | + | Кроме того, в `{$mode objFPC}` подпрограмма [`exit`](https://www.freepascal.org/docs-html/rtl/system/exit.html) также установит возвращаемое значение *и* покинет стековый кадр. В предыдущих двух примерах могли появиться дополнительные операторы, и они были бы выполнены, уже после того, как `exit` подпрограмма *выполнится*. Это поведение оператора `return` в C или других языках программирования. | |
| 40 | + | ||
| 41 | + | ``` pascal | |
| 42 | + | // использование процедуры выхода exit | |
| 43 | + | function even(const x: longint): Boolean; | |
| 44 | + | begin | |
| 45 | + | exit(not odd(x)); | |
| 46 | + | end; | |
| 47 | + | ``` | |
| 48 | + | ||
| 49 | + | В языке [[Assembly language|ассемблера]] применяются другие правила. Если тип возвращаемого значения является целым числом, используется регистр-накопитель, при условии, что он помещается туда: | |
| 50 | + | ||
| 51 | + | ``` pascal | |
| 52 | + | // на ассемблере: | |
| 53 | + | // возвращаемый тип умещается в один регистр => использовать регистр-накопитель | |
| 54 | + | function zero(const x: int64): Boolean; | |
| 55 | + | {$ifDef CPUx86_64} | |
| 56 | + | assembler; register; | |
| 57 | + | {$asmMode intel} | |
| 58 | + | asm | |
| 59 | + | // xor изменяет флаги => поместите его перед тестом | |
| 60 | + | xor rax, rax // rax := 0 (удалить остаток) | |
| 61 | + | ||
| 62 | + | test x, x // x = 0 ? | |
| 63 | + | setz al // rax := ZF | |
| 64 | + | ||
| 65 | + | // Когда вы изучаете вывод ассемблера | |
| 66 | + | // вы заметите, что компилятор автоматически вставляет код | |
| 67 | + | // который перемещает содержимое rax в нужное место в стеке, | |
| 68 | + | // пока не появится подсказка noStackFrame | |
| 69 | + | // (автоматически устанавливается некоторыми уровнями оптимизации) | |
| 70 | + | // инструктирует компилятор опустить кадр стека, если это возможно. | |
| 71 | + | end; | |
| 72 | + | {$else} | |
| 73 | + | begin | |
| 74 | + | result := x = 0; | |
| 75 | + | end; | |
| 76 | + | {$endIf} | |
| 77 | + | ``` | |
| 78 | + | ||
| 79 | + | В противном случае, в зависимости от того, какой режим [[sAsmmode|{$asmMode}]] активен, можно использовать макрос `@result` (Intel) или `__result` (AT&T). | |
| 80 | + | ||
| 81 | + | ``` pascal | |
| 82 | + | type | |
| 83 | + | bodyAttributes = record | |
| 84 | + | surfaceArea: real; | |
| 85 | + | volume: real; | |
| 86 | + | end; | |
| 87 | + | ||
| 88 | + | // на ассемблере: | |
| 89 | + | // возвращаемый тип не помещается в аккумулятор => макрос @result дает адрес | |
| 90 | + | function sphere(const radius: real): bodyAttributes; | |
| 91 | + | {$ifDef CPUx86_64} | |
| 92 | + | assembler; | |
| 93 | + | {$asmMode intel} | |
| 94 | + | const | |
| 95 | + | three: longint = 3; | |
| 96 | + | four: longint = 4; | |
| 97 | + | var | |
| 98 | + | r: real; | |
| 99 | + | asm | |
| 100 | + | pextrq r, radius, 0 // r := (@radius+0)^ | |
| 101 | + | lea rax, @result // rax := @result | |
| 102 | + | fld r // radius | |
| 103 | + | ||
| 104 | + | fld st(0) // radius radius | |
| 105 | + | fild four // 4 radius radius | |
| 106 | + | fldpi // pi 4 radius radius | |
| 107 | + | fmul // 4*pi radius radius | |
| 108 | + | fxch // radius 4*pi radius | |
| 109 | + | fld st(0) // radius radius 4*pi radius | |
| 110 | + | fmul // radius^2 4*pi radius | |
| 111 | + | fmul // 4*pi*radius^2 radius | |
| 112 | + | fst [rax].bodyAttributes.surfaceArea | |
| 113 | + | ||
| 114 | + | fmul // 4*pi*radius^3 | |
| 115 | + | fild three // 3 4*pi*radius^3 | |
| 116 | + | fdivp // 4/3*pi*radius^3 | |
| 117 | + | fst [rax].bodyAttributes.volume | |
| 118 | + | end; | |
| 119 | + | {$else} | |
| 120 | + | begin | |
| 121 | + | sphere.surfaceArea := 4 * pi() * sqr(radius); | |
| 122 | + | sphere.volume := 4 / 3 * pi() * sqr(radius) * abs(radius); | |
| 123 | + | end; | |
| 124 | + | {$endIf} | |
| 125 | + | ``` | |
| 126 | + | ||
| 127 | + | Первоначально Паскаль ожидал *ровно одно* [[Becomes|присваивание]] переменной результата (в зависимости от того, что используется). Однако [[FPC]] не запрещает множественные присваивания. Он выдаст предупреждение, если не был использован ни один из возможных идентификаторов результата или процедура выхода не написана. | |
| 128 | + | ||
| 129 | + | > Warning | |
| 130 | + | > Function result does not seem to be set (*рус*. Предупреждение: результат функции, похоже, не установлен) | |
| 131 | + | > | |
| 132 | + | > 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|компилятор]] считает, что возвращаемое значение функции не установлено. Это не будет отображаться для ассемблерных процедур или процедур, содержащих ассемблерные блоки.) | |
| 133 | + | ||
| 134 | + | ``` pascal | |
| 135 | + | begin | |
| 136 | + | writeLn(sphere(2.0).surfaceArea); | |
| 137 | + | end. | |
| 138 | + | ``` | |
| 139 | + | ||
| 140 | + | Обратите внимание, что в случае [[Operator overloading|перегрузок операторов]] в формальной подписи должен быть объявлен специальный вид функции, идентификатор переменной результата. Подробнее см. в статье. | |
| 141 | + | ||
| 142 | + | ## Замечания | |
| 143 | + | ||
| 144 | + | [[Pure functions]] это функции, не зависящие от внешнего состояния. | |
| 145 | + | ||
| 146 | + | ## См.также | |
| 147 | + | ||
| 148 | + | - [[Pascal basics]] | |
| 149 | + | - [§ “Using functions and procedures” in the Reference Guide](https://www.freepascal.org/docs-html/ref/refch14.html) | |
| 150 | + | - [§ “Intel 80x86 Inline assembler” in the Programmer’s Guide](https://www.freepascal.org/docs-html/prog/progse10.html) | |
| 151 | + | ||
| 152 | + | --- | |
| 153 | + | *From the Free Pascal wiki, page "Function/ru", as it stood on 2022-05-31 after a change by Zoltanleo, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
