Functions
Tusk programs have access to a vast collection of routines
(procedures and function) defined in Delphi, including…
- Delphi intrinsic routines such as Abs and Ord,
- Standard Delphi RTL routines such as Pos and StrToInt,
- Familiar in-house library routines such as HasText and DSFormat,
- Application-specific routines, exposed by the host program.
Tusk allows a trailing semi-colon when defining
a routine's parameters…
procedure Work(
x: Integer = 0;
y: Integer = 1;
z: Integer = 2;
);
begin
Writeln(x);
Writeln(y);
Writeln(z);
end;
In Tusk, a function has an automatically defined
variable named Result, which holds the function's
result.
This variable is always zero-initialized
(it never holds a "garbage" value).
Procedures do not have access to Result,
except from an enclosing scope…
function IntToStr(x: Integer): string;
procedure HandleNegative;
begin
Result := '-' + Result;
end;
var
i: Integer;
begin
Result := '';
i := Abs(x);
while i > 0 do begin
Result := Chr(Ord('0') + i mod 10) + Result;
i := i div 10;
end;
if x < 0 then
HandleNegative;
end;
A function cannot have a parameter or local variable or constant
named Result (this restriction doesn't apply to inline
variables and constants).
The other way to specify a function's return value
is with the Exit statement…
function IsPrime(n: Integer): Boolean;
begin
if (n < 2) or (n mod 2 = 0) then
Exit(False);
if n < 4 then
Exit(True);
for var i := 3 to n div 2 do
if n mod i = 0 then
Exit(False);
Result := True;
end;
Exit may be used without an expression,
in which case the function terminates early,
returning whatever value is currently in Result…
function CalculateShippingCost(
Weight: Double; Distance: Integer): Double;
begin
Result := 5.00; // Base shipping cost
if Weight <= 0 then
Exit; // Return base cost for invalid weight
if Distance <= 0 then
Exit; // Return base cost for invalid distance
// Calculate actual cost based on weight and distance
Result := Result + (Weight * 0.50) + (Distance * 0.10);
end;
The Exit statement may also be used in procedures,
in which case the argument must be omitted…
procedure LogError(const Msg: string);
begin
if Msg = '' then
Exit; // Nothing to log
Writeln('[ERROR] ', Msg);
AppendToFile('error.log', Msg);
end;
Tusk allows multiple versions of a routine with the same name,
but different numbers and/or types of parameters.
For example…
procedure Emit(const Value: string); overload;
begin
Writeln(QuoteStr(Value, ''''));
end;
procedure Emit(Value: TDateTime); overload;
begin
Writeln('#(', DateTimeToStr(Value), ')');
end;
Above, Emit may be called with a string
or with a TDateTime.
Note the presence of the overload directive.
As in Delphi, this engages the overloading mechanism.
Additional details on overloading can be found
here.
Tusk allows a routine to be declared and called
before its definition, allowing for mutually recursive
routines.
For example…
function ParseFactor(
const S: string; var Pos: Integer): Double; forward;
function ParseExpr(
const S: string; var Pos: Integer): Double;
begin
Result := ParseFactor(S, Pos);
// Skip whitespace
while (Pos <= Length(S)) and (S[Pos] in dscsWhiteSpace) do
Inc(Pos);
// Handle addition
while (Pos <= Length(S)) and (S[Pos] = '+') do begin
Inc(Pos); // Skip '+'
Result := Result + ParseFactor(S, Pos);
while (Pos <= Length(S)) and (S[Pos] = ' ') do
Inc(Pos);
end;
end;
function ParseFactor(
const S: string; var Pos: Integer): Double;
const
DigitOrDot = dscsDigit + '.';
var
StartPos: Integer;
NumStr: string;
begin
// Skip whitespace
while (Pos <= Length(S)) and (S[Pos] in dscsWhitespace) do
Inc(Pos);
// Handle parenthesized expression
if S[Pos] = '(' then begin
Inc(Pos); // Skip '('
Result := ParseExpr(S, Pos); // Recurse to expression parser
Inc(Pos); // Skip ')'
end else begin
// Parse number
StartPos := Pos;
while (Pos <= Length(S)) and (S[Pos] in DigitOrDot) do
Inc(Pos);
NumStr := Copy(S, StartPos, Pos - StartPos);
Result := StrToFloat(NumStr);
end;
end;
function EvalExpr(const S: string): Double;
begin
var Pos := 1;
Result := ParseExpr(S, Pos);
end;
Writeln(EvalExpr('5+3')); // prints 8
Writeln(EvalExpr('(10+5) + 3')); // prints 18
Above, ParseFactor and ParseExpr each calls the other,
so one must be forward declared (in this case, ParseFactor).
Tusk offers slightly more concise notation for defining
short functions and procedures…
// Traditional approach
function Sum(x, y: Integer): Integer;
begin
Result := x + y;
end;
// More concise alternative
function Sum(x, y: Integer): Integer = x + y;
// Traditional approach
procedure Display(a, b: Integer);
begin
Writeln(a, ' + ', b, ' = ', Sum(a, b));
end;
// More concise alternative
procedure Display(a, b: Integer) =
Writeln(a, ' + ', b, ' = ', Sum(a, b));
For a one-line procedure, what follows the = sign
is a single statement.
For a one-line function, what follows the = sign
is a single expression.
Tusk supports type inference for the return type
of a function when using the one-line approach…
// Implicitly returns Integer
function Sum(x, y: Integer) = x + y;
Note that this feature is not compatible
with recursive functions.
So, instead of this…
function f(x: Integer) =
if x=0 then 0 else x+f(x-1);
you'll need to do this…
function f(x: Integer): Integer =
if x=0 then 0 else x+f(x-1);
Like Delphi, Tusk supports anonymous methods
(routines defined "inline", as an expression,
often passed as an argument to another routine).
For example…
var ls: IStringList := NewStringList;
ls.Add('apple');
ls.Add('banana');
ls.Add('cherry');
ls.Add('date');
ls.Add('fig');
ls.Add('grape');
// Prints: apple,banana,cherry,date,fig,grape
Writeln(ls.CommaText);
ls.DeleteWhere(
function(const s: string): Boolean
begin
Result := Length(s) <= 4;
end);
// Prints: apple,banana,cherry,grape
Writeln(ls.CommaText);
Above, the call to DeleteWhere passes an anonymous method.
Anonymous methods are compatible with the one-liner notation,
so the above call to DeleteWhere could be shortened to…
ls.DeleteWhere(
function(const s: string) = Length(s) <= 4);
The keyword nil is assignment-compatible
with all anonymous method types.
⏱ Last Modified: 2/15 10:06:11 am