Functions
A summary of some of the features of functions in CellSpeak:
- functions can have multiple return values
- functions can be constant or variable functions
- there is a short form of a function definition
- functions can capture data in their scope at definition
- functions, constant or variable, can be used as parameters
- functions can be standalone, part of a design or part of a type (methods)
- functions can be nested
- functions can be used to define operators
(1) If you want to give names to multiple return values, then the total return spec must be put between brackets.
Without names you can also write: function Divide( int a, int b ) out float, ansi is
In any case the names are for information only - no variables are declared.
(2) The compiler selects the most efficient way of passing parameters - by value or by reference. Arrays and records
are always passed by reference, irrespective whether they can be changed or not.
The compiler will check that a parameter that is an in parameter, is not modified in the function.
(3) We can define a record inside a record - makes for a compact definition where we do not have to define types for everything.
(4) This is how we initialize the sub-record that we have defined as part of the record.
(5) This is a short function - will be inlined by the compiler.
(6) The name of a variable function - like any variable - must be unique, that is why we only need the name to refer to the function variable.
Being able to change the body of a function can make code both more readable and faster at execution.
For example, depending on some initial checks and conditions, different versions of a function can be plugged in,
thus avoiding that checks need to repeated at every invocation of the function.
(7) The withpart follows after the function header. It contains a list of data that is permanent in the function,
i.e. that is remains intact between function invocations. The space for that data is released automatically when the function goes out fo scope.
(8) When there is only a keep part in the data section- capturing variables that are in scope - then the with .. end
can be omitted.
(9) When a function is not defined immediately, then the declaration has to be followed by to do.
A function definition like function ConicSection( float x, float y) out float to do can be used as the
template for other function definitions e.g. function Ellipse like ConicSection is ... .
This is how function types are implemented in CellSpeak.
(10) The function IFlyToo gets its own copy of the local data of Fly.
With a simple assignment, both functions would share the same data.
(11) The function f is a parameter.
In a function definition as a parameter, the return values have to be enclosed in parentheses to avoid ambiguity with the rest of the parameter spec.
05 Functions.celsrc
use Windows, Math, Strings, Editor
group Functions
design Demonstrator is
cell Window
constructor is
Window = create MenuWindow("Function Demonstrator")
Window <- print("\n\n*** Test1: the basics\n")
function Add( int a, int b ) out int is
return a + b
end
function Subtract( int a, int b ) out ( int Result ) is
return a - b
end
function Multiply(int a, int b) => a * b
(1) function Divide( int a, int b ) out ( float Result, ansi Error ) is
if b is 0 then
return inf_f, "Division by zero !")
else
return a/b, null
end
end
var Result, Error = Divide( 56,0)
Window<-print("\nThe function call returns: [Error ? Error : \"\" ] [Result]")
Result, Error = Divide( 56, 7)
Window<-print("\nThe function call returns: [Error ? Error : \"\" ] [Result]")
function Factorial( int n) out int => n ? n*Factorial(n-1) : 1
Window <- print("\nFactorial(7) returns = [Factorial(7)]")
(2)
Window <- print("\n\n*** Test2: In and out parameters\n")
type PersonRecord is record
ansi Name
(3) record
int Day
ansi Month
int Year
end Birthday
ansi SoundAdvice
ansi ZodiacSign
end
function Numerology(out PersonRecord Person, int A, int B) out (int LifePathNumber, float HeartsDesireNumber) is
Person.Name.upper()
if Person.Birthday.Month is "january" and Person.Birthday.Day >= 20 then
Person.ZodiacSign = "aquarius"
else
Person.ZodiacSign = "squid"
end
Person.SoundAdvice = "Watch out for venomous snakes on the [Person.Birthday.Day] day of each month")
return Person.Birthday.Year % Person.Birthday.Day , (A^2 + B^2 - A*B)
end
PersonRecord Me = [
"Sam Verstraete",
(4) [23,"january",1958],
null,
null]
var LifePathNumber, HeartsDesireNumber = Numerology(Me, 5, 4)
Window <- print("
[Me.Name], these are your results:
Your astrological sign is [Me.ZodiacSign]
Your Life Path Number is [LifePathNumber]
Your Heart's Desire Number is [HeartsDesireNumber]
And always follow this sound advice : [Me.SoundAdvice]")
Window <- print("\n\n*** Test3: Nested functions\n")
function QuadraticRoots( float A, float B, float C ) out (complex Root1, complex Root2) is
(5) function Discriminant( float A, float B, float C ) => B^2 - 4*A*C
var D = Discriminant(A,B,C)
var I = sqrt(abs(D)) / (2*A)
var R = -B / (2*A)
if D > 0 then
return [ R+I, 0.0], [R-I, 0.0]
elif D < 0 then
return [R, I], [R, -I]
else
return [R, 0], [R, 0]
end
end
for each A in [2.0, -2.0] do
for each B in [4.0, -4.0] do
for each C in [8.0, -8.0] do
var R1, R2 = QuadraticRoots( A, B, C)
Window <- print("\nThe roots of the equation y = [A,%2.1f].x^2 [B,%+2.1f].x [C,%+2.1f] are ([R1.r], [R1.i]) and ([R2.r], [R2.i])")
end
end
end
Window <- print("\n\n*** Test5: Function variables\n")
var function F1( xyz V1, xyz V2) out float is
return V1*V2
end
xyz V1=[1,2,3], V2=[4,7,2]
Window<-print("\nResult for F1(V1, V2) is [F1(V1, V2)]")
(6) body F1 is
return (V1#V2).length()
end
Window <- print("\nChanged the body of F1, result for F1(V1, V2) is now [F1(V1, V2)]")
function F2( xyz U1, xyz U2 ) out float is
return (U1 + U2).length()
end
Window <- print("\nF2 is a new constant function. Result for F2(V1, V2) is now [F2(V1, V2)]")
F1 = F2
Window <- print("\nResult for F1(V1, V2) after F1 = F2 is also [F1(V1, V2)]")
var function F3( xyz U1, xyz U2 ) out float is
return (U1 - U2).length()
end
Window <- print("\nF3 is a new variable fucntion. Result for F3(V1, V2) is now [F3(V1, V2)]")
F1 = F3
Window <- print("\nResult for F1(V1, V2) after F1 = F3 is also [F1(V1, V2)]")
Window <- print("\n\n*** Test5: Closures and function fields\n")
int ListPrice = 300
ansi Airplane = "Boeing 747"
function Fly(ansi Destination)
(7) with
keep Airplane, Window
ansi Airline = "KLM"
ansi From = "Amsterdam"
end
is
Destination is "Chicago" ? Airline = "Delta Airlines"
Window <- print("\nI fly from [From] to [Destination] with a [Airplane] from [Airline].")
From = Destination
end
function Buy(ansi Seller)
(8) keep Airplane, ListPrice, Window
is
Window <- print("\nI bought a [Airplane] from [Seller] for about [ListPrice] MUSD.")
end
Fly("Berlin")
var function AirplaneAction(ansi What) to do
AirplaneAction = Fly
AirplaneAction("New York")
AirplaneAction = Buy
AirplaneAction("my neighbour")
Window <- print("\n\nFunctions with a copy of the captured data\n")
(9) var function IFlyToo(ansi Where) to do
(10) new IFlyToo = Fly
Fly("Chicago")
Fly("Tokio")
IFlyToo("Paris")
IFlyToo("Bejing")
Window <- print("\n\n*** Test6: Functions as parameters\n")
type NumberList is int[] with
function Populate is
for each [i] in this => this[i] = i
end
(11) function Apply(function f(int) out (int), float a) is
for each N in this => N = a*f(N)
end
function print(cell W) is
W <- print("\nThe number list:\n")
for each N in this => W <- print(" [N]")
end
end
NumberList[20] MyList
MyList.Populate()
function Square(int x) => x^2
MyList.Apply( Square, 10.0 )
MyList.print(Window)
end
end