4D v16.3Project Methods |
|||||||||||||||||||||||
|
4D v16.3
Project Methods
|
Type of parameter | How passed | Comment |
Field, variable or expression of a scalar type (number, text, date...) | by value | Can be passed by reference through a pointer, see below |
Field, variable or expression of type Object | by reference | See example below |
Variable or expression of type Collection | by reference | |
Variable or expression of type Pointer | by reference | See Passing Pointers to Methods |
Array | Cannot be passed directly as parameter | Can be passed by reference through a pointer, see Arrays and Pointers |
Table | Cannot be passed directly as parameter | Can be passed by reference through a pointer, see Pointers |
When using fields, variables and expressions of the scalar type as method parameters, only copies of values are passed.
Since $1, $2... are local variables, they are available only within the subroutine and are cleared at the end of the subroutine. For this reason, a subroutine cannot change the value of the actual fields or variables passed as parameters at the calling method level. For example:
` Here is some code from the method MY METHOD
` ...
DO SOMETHING([People]Last Name) ` Let's say [People]Last Name is equal to "williams"
ALERT([People]Last Name)
` Here is the code of the method DO SOMETHING
$1:=Uppercase($1)
ALERT($1)
The alert box displayed by DO SOMETHING will read “WILLIAMS” and the alert box displayed by MY METHOD will read “williams”. The method locally changed the value of the parameter $1, but this does not affect the value of the field [People]Last Name passed as parameter by the method MY METHOD.
There are two ways to make the method DO SOMETHING change the value of the field:
1. Rather than passing the field to the method, you pass a pointer to it, so you would write:
` Here is some code from the method MY METHOD
` ...
DO SOMETHING(->[People]Last Name) ` Let's say [People]Last Name is equal to "williams"
ALERT([People]Last Name)
` Here the code of the method DO SOMETHING
$1->:=Uppercase($1->)
ALERT($1->)
Here the parameter is not the field, but a pointer to it. Therefore, within the DO SOMETHING method, $1 is no longer the value of the field but a pointer to the field. The object referenced by $1 ($1-> in the code above) is the actual field. Consequently, changing the referenced object goes beyond the scope of the subroutine, and the actual field is affected. In this example, both alert boxes will read “WILLIAMS”.
For more information about Pointers, see the section Pointers.
2. Rather than having the method DO SOMETHING “doing something,” you can rewrite the method so it returns a value. Thus you would write:
` Here is some code from the method MY METHOD
` ...
[People]Last Name:=DO SOMETHING([People]Last Name) ` Let's say [People]Last Name is equal to "williams"
ALERT([People]Last Name)
` Here the code of the method DO SOMETHING
$0:=Uppercase($1)
ALERT($0)
This second technique of returning a value by a subroutine is called “using a function.” This is described in the next paragraphs.
When using variables, expressions, or fields of the object or collection type as method parameters, references to actual source values are passed. In this case, $1, $2... do not contain values but references. Modifying the value of the $1, $2... parameters within the subroutine will be propagated wherever the source object or collection is used. This is the same principle as for pointers, except that $1, $2... parameters do not need to be dereferenced in the subroutine.
For example:
//The CreatePerson method creates an object and sends it as a parameter
C_OBJECT($person)
$person:=New object("Name";"Smith";"Age";40)
ChangeAge($person)
ALERT(String(OB get($person;"Age")))
//The ChangeAge method adds 10 to the Age attribute of the received object
C_OBJECT($1)
OB SET($1;"Age";OB Get($1;"Age")+10)
ALERT(String(OB get($1;"Age")))
If you execute the CreatePerson method, both alert boxes will read "50" since the same reference is handled by both methods.
4D Server: When parameters are passed between methods that are not executed on the same machine (using for example the Execute on Server option, see Project method properties), references are not usable. In these cases, copies of object and collection parameters are sent instead of references.
Data can be returned from methods. A method that returns a value is called a function.
4D or 4D Plug-in commands that return a value are also called functions.
For example, the following line is a statement that uses the built-in function, Length, to return the length of a string. The statement puts the value returned by Length in a variable called MyLength. Here is the statement:
MyLength:=Length("How did I get here?")
Any subroutine can return a value. The value to be returned is put into the local variable $0.
For example, the following function, called Uppercase4, returns a string with the first four characters of the string passed to it in uppercase:
$0:=Uppercase(Substring($1;1;4))+Substring($1;5)
The following is an example that uses the Uppercase4 function:
NewPhrase:=Uppercase4("This is good.")
In this example, the variable NewPhrase gets “THIS is good.”
The function result, $0, is a local variable within the subroutine. It can be used as such within the subroutine. For example, in the previous DO SOMETHING example, $0 was first assigned the value of $1, then used as parameter to the ALERT command. Within the subroutine, you can use $0 in the same way you would use any other local variable. It is 4D that returns the value of $0 (as it is when the subroutine ends) to the called method.
Project methods can call themselves. For example:
This is called recursion. The 4D language fully supports recursion.
Here is an example. Let’s say you have a [Friends and Relatives] table composed of this extremely simplified set of fields:
- [Friends and Relatives]Name
- [Friends and Relatives]ChildrensName
For this example, we assume the values in the fields are unique (there are no two persons with the same name). Given a name, you want to build the sentence “A friend of mine, John who is the child of Paul who is the child of Jane who is the child of Robert who is the child of Eleanor, does this for a living!”:
1. You can build the sentence in this way:
$vsName:=Request("Enter the name:";"John")
If(OK=1)
QUERY([Friends and Relatives];[Friends and Relatives]Name=$vsName)
If(Records in selection([Friends and Relatives])>0)
$vtTheWholeStory:="A friend of mine, "+$vsName
Repeat
QUERY([Friends and Relatives];[Friends and Relatives]ChildrensName=$vsName)
$vlQueryResult:=Records in selection([Friends and Relatives])
If($vlQueryResult>0)
$vtTheWholeStory:=$vtTheWholeStory+" who is the child of "+[Friends and Relatives]Name
$vsName:=[Friends and Relatives]Name
End if
Until($vlQueryResult=0)
$vtTheWholeStory:=$vtTheWholeStory+", does this for a living!"
ALERT($vtTheWholeStory)
End if
End if
2. You can also build it this way:
$vsName:=Request("Enter the name:";"John")
If(OK=1)
QUERY([Friends and Relatives];[Friends and Relatives]Name=$vsName)
If(Records in selection([Friends and Relatives])>0)
ALERT("A friend of mine, "+Genealogy of($vsName)+", does this for a living!")
End if
End if
with the recursive function Genealogy of listed here:
` Genealogy of project method
` Genealogy of ( String ) -> Text
` Genealogy of ( Name ) -> Part of sentence
$0:=$1
QUERY([Friends and Relatives];[Friends and Relatives]ChildrensName=$1)
If(Records in selection([Friends and Relatives])>0)
$0:=$0+" who is the child of "+Genealogy of([Friends and Relatives]Name)
End if
Note the Genealogy of method which calls itself.
The first way is an iterative algorithm. The second way is a recursive algorithm.
When implementing code for cases like the previous example, it is important to note that you can always write methods using iteration or recursion. Typically, recursion provides more concise, readable, and maintainable code, but using it is not mandatory.
Some typical uses of recursion in 4D are:
Important: Recursive calls should always end at some point. In the example, the method Genealogy of stops calling itself when the query returns no records. Without this condition test, the method would call itself indefinitely; eventually, 4D would return a “Stack Full” error becuase it would no longer have space to “pile up” the calls (as well as parameters and local variables used in the method).
Product: 4D
Theme: Language definition
4D Language Reference ( 4D v16)
4D Language Reference ( 4D v16.1)
4D Language Reference ( 4D v16.2)
4D Language Reference ( 4D v16.3)