Function/fi

From Lazarus wiki

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

Funktio eli function on rutiini, joka toisin kuin aliohjelma (procedure) palauttaa arvon. Funktion kutsu käytännössä korvautuu sen palautusarvolla. Jos {$extendedSyntax} kääntäjän kytkin (compiler switch) on pois päältä, funktion kutsu ei voi esiintyä ei-tuottavina lauseina, vaan niiden on oltava osa lauseketta.

Sana function on varattu sana.

Palautusarvo

Normaalin aliohjelman esittelyn lisäksi funktion esittely sisältää myös palautustyypin: muodollisen parametriluettelon jälkeen tulee kaksoispiste- ja palautustyyppi. Seuraava funktion esittely palauttaa esimerkiksi boolean tyyppisen arvon.

function myFunction(const firstParameter: real): boolean;

Funktion toteuttamisessa on useita tapoja toteuttaa funktion palautusarvo.

 1 program functionDemo(input, output, stderr);
 2 
 3 {$mode objfpc}
 4 
 5 // Perinteinen tapa:
 6 // palautus arvo tallennetaan muuttujaan 
 7 // jonka nimi on sama kuin funktion nimi
 8 function myLine(const x: real): real;
 9 begin
10 	myLine := 0.5 * x + 2;
11 end;

Jos {$modeswitch result+} on käytössä. Se asetetaan {$mode objFPC} ja {$mode Delphi}, niin toteutuslohkon sisällä on käytössä tunniste result myöskin:

13 // käytetään `result` tunnistetta
14 function myParabola(const x: real): real;
15 begin
16 	result := sqr(x) - 1;
17 end;

Lisäksi {$mode objFPC}:ssa rutiini exit asettaa myös palautusarvon ja jättää pinoon kehyksen. Kahdessa aikaisemmassa esimerkissä olisi voinut esiintyä muita lausekkeita ja ne olisi toteutettu, kun taas exit:n jälkeen palataan funktiosta pois. Tämä on samanlainen käyttäytyminen kuin C:n tai joidenkin muiden ohjelmointikielien return lausekkeen käyttäytyminen.


19 // käytetään exit rutiinia
20 function even(const x: longint): boolean;
21 begin
22 	exit(not odd(x));
23 end;

Assembly kielen yhteydessä sovelletaan muita sääntöjä. Jos palautustyyppi on kiinteä arvo, käytetään akku-rekisteriä, jos se mahtuu sinne:

25 // in assembly language:
26 // return type fits into a single register => use accumulator register
27 function zero(const x: int64): boolean;
28 {$ifdef CPUx86_64}
29 assembler; register;
30 {$asmMode intel}
31 asm
32 	// xor modifies flags => put it in front of test
33 	xor rax, rax     // rax := 0 [false]
34 	
35 	// examining the assembler output
36 	// we can verify x is stored in register rdi [i.e. not rax]
37 	test x, x        // x = 0 ?
38 	jnz @zero_done   // if x <> 0 then goto done
39 	
40 	inc rax          // rax := 1 [true]
41 @zero_done:
42 	// When you examine the assembler output
43 	// you will notice the compiler automatically inserts code
44 	// that moves the contents of rax to the right spot on the stack.
45 end;
46 {$else}
47 begin
48 	// NOTE: with optimization switches enabled
49 	//       the compiler produces with the following Pascal statement
50 	//       even shorter (and maybe faster) code
51 	//       than the assembler implementation above
52 	result := x = 0;
53 end;
54 {$endif}

Muuten riippuen siitä, mikä {$asmMode} on aktiivinen, @result (Intel) tai __result (AT&T) -makroa voidaan käyttää.

56 type
57 	bodyAttributes = record
58 		surfaceArea: real;
59 		volume: real;
60 	end;
61 
62 // in assembly language:
63 // return type doesn't fit into accumulator => @result macro gives address
64 function sphere(const radius: real): bodyAttributes;
65 {$ifdef CPUx86_64}
66 assembler;
67 {$asmMode intel}
68 const
69 	three: longint = 3;
70 	four: longint = 4;
71 var
72 	r: real;
73 asm
74 	pextrq r, radius, 0 // r := (@radius+0)^
75 	lea rax, @result    // rax := @result
76 	fld r               // radius
77 	
78 	fld st(0)           // radius radius
79 	fild four           // 4 radius radius
80 	fldpi               // pi 4 radius radius
81 	fmul                // 4*pi radius radius
82 	fxch                // radius 4*pi radius
83 	fld st(0)           // radius radius 4*pi radius
84 	fmul                // radius^2 4*pi radius
85 	fmul                // 4*pi*radius^2 radius
86 	fst [rax].bodyAttributes.surfaceArea
87 	
88 	fmul                // 4*pi*radius^3
89 	fild three          // 3 4*pi*radius^3
90 	fdivp               // 4/3*pi*radius^3
91 	fst [rax].bodyAttributes.volume
92 end;
93 {$else}
94 begin
95 	sphere.surfaceArea := 4 * pi() * sqr(radius);
96 	sphere.volume := 4 / 3 * pi() * sqr(radius) * abs(radius);
97 end;
98 {$endif}

Alun perin Pascal odotti täsmällisesti yhden tehtävän tulosmuuttujalle. FPC ei kuitenkaan kiellä käyttämästä useita. Se lähettää varoituksen, jos mikään mahdollisista palautumisarvoista ei ole käytössä tai poistumisrutiinia ei ole kirjoitettu.

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.
100 begin
101 	writeLn(sphere(2.0).surfaceArea);
102 end.

Katso myös