Punctuation in Erlang
The Erlang syntax is full of conventions. A name starting with an upper-case letter (such as address) represents a variable, including parameters and local variables. A word starting with a lower-case letter (such as OK) represents a constant, it is called atom, including the constant name, function name, and module name.
Erlang Comments start with %. Erlang uses the underscore "_" to represent any variable, similar to the default option in the switch Syntax of Java.
Erlang originated from Prolog. However, I think the Erlang syntax is similar to the Haskell syntax, and both use the-> definition function.
The usage of punctuation in Erlang statements is similar to that in the document.
The end of the entire function definition is a period (.). The logical branches in the same function are separated by semicolons (;). The sequential statements are separated by commas.
In Erlang, {} is not the beginning and end of the program block, but a special data structure type-tuple (tuples), for example, {12, 3, OK }. We can understand tuple as a fixed-length array.
[] Indicates the data structure type of the most basic functional programming-list. The List data structure is very basic, and the writing and usage are also complex. It is not as simple as it looks on the surface. The following section describes the basic construction principle of the list in detail.
Let's take a simple example.
First, we define a simplest function, multiply a parameter by 10, and then add 1.
Times10 (number)->
Temp = 10 * number,
Temp + 1.
To illustrate the problem, the above Code divides multiplication and addition operations into two steps. Temp = 10 * number is followed by a comma, because these are two statements executed in sequence. The end of the temp + 1 statement indicates that the entire function definition ends. In addition, we can see that Erlang does not have a return statement, and the execution result of the last statement executed is the return value.
Next, we will optimize this function. When the parameter is equal to 0, 1 is returned directly; otherwise, 10 is multiplied, 1 is added, and then return. At this time, we need to use the case of logical branch statement, which is equivalent to the switch statement of Java.
Times10 (number)->
Case number
0-> 1;
_->
Temp = 10 * number,
Temp + 1
End.
Let's take a closer look at this Erlang program.
If number is equal to 0, 1 is returned directly. Because this is a branch statement, it is in parallel with the following branch, so the punctuation mark behind 1 is a semicolon. Next to this branch, the underscore "_" indicates any other value. Here it indicates any other value except 1.
Note that the case of statement must end with end, and there is no punctuation before end.
The case of statement in the above Code is actually a pattern match. Erlang's pattern match is very powerful and can greatly simplify the program logic, which will be introduced later.
Pattern Match
Pattern match has two main functions: Comparison assignment and variable assignment.
Comparison and distribution are the main functions. Comparison dispatch means that the condition branch is distributed based on the parameter value. The comparison and dispatch function can be considered as a simple and powerful way to write branch statements similar to if, else, and other conditions.
In the above example, case number of is to compare and dispatch the data based on the value of number. A more common way is to mention the pattern match part to the height of the function-defined branch. Therefore, the above Code can be written in the following form:
Times10 (0)-> 1;
Times10 (number)->
Temp = 10 * number,
Temp + 1.
This Code consists of two function-defined branches. Because the function names of the two function branches are the same and the number of parameters is the same, the two function-defined branches are separated by semicolons, this is the definition of the same function. In functional programming languages, this definition method is common and looks neat like a mathematical formula.
The meaning of this Code is that when the parameter value is equal to 0, the program goes through the first function definition Branch (that is, the semicolon ";" ending with "times10 (0)-> 1; ). Otherwise, go down to the Function Definition Branch (that is, "times10 (number)-> ...").
The parameter in the second branch is not a constant, but a variable number, indicating that the branch can accept any parameter values except 0, such as 1, 2, 12, etc, these values are assigned to the variable number.
Therefore, this part also shows the second function of pattern match-variable assignment.
The pattern match format can be very complex. The following are some typical examples.
(1) Data Structure disassembly assignment
In Erlang, there is a tuple type equivalent to a fixed-length array. We can conveniently assign values in parallel based on the position of elements. For example,
{First, second }={ 1, 2}
We can also assign values to the composite tuple data structure, for example
{A, {B, c}, d} = {1, {2, 3}, 4}
The assignment of the List data structure is similar. Since the writing and usage of list are not that simple, I am not clear about it in a few words, but I am still suffering from increasing troubles. I will not go into details here.
(2) assertequals statement
In Java and other languages, when we write unit tests, we will write some assert statements to verify the program running results. These assert statements are generally provided as APIS, such as asserttrue () and assertequals.
In Erlang, you can use simple statements to achieve the effects of Apis such as asserttrue () and assertequals.
For example, in Erlang, a statement such as true = TESTA () indicates that the returned result of Testa must be true; otherwise, an exception is thrown. This usage is clever. Here is an explanation.
As mentioned earlier, the Erlang syntax stipulates that the names starting with lowercase letters are constant names. Here, true is also a constant. Since it is a constant, we cannot assign a value to it. Therefore, true = TESTA () does not mean a value assignment, but a matching comparison.
(3) Matching and assigning values simultaneously
Let's look at this piece of code.
Case result
{OK, message}-> Save (Message );
{Error, errormessage}-> log (errormessage)
End.
In this Code, result is a tuple type and contains two elements. The first element indicates success or error, and the second element indicates specific information.
We can see that both constants and variables appear in the two condition branches. In the first condition branch, OK is a constant, message is a variable, error is a constant in the second condition branch, and errormessage is a variable.
Both condition branches have both comparative judgment and variable assignment. First, determine the first element in resulttuple that matches the first element of the branch. If it matches, assign the second element in resulttuple to the second variable element of the branch. That is, if the first element of result is OK, take the first condition branch and assign the second element of result to the Message variable. If the second element of result is error, take the second condition branch and assign the second element of result to the errormessage variable.
In Java and other languages, to implement the preceding conditional branch logic, you need to write several more statements. The Erlang syntax can beautify and simplify the complex program of logical branch assignment in form.
In addition to equal number comparison, pattern match also supports range comparison and size comparison. The keyword when is required. However, when is used, it is less concise than if Else, I will not go into details here.
Anonymous Functions
Erlang Allows defining another anonymous function within a function body, which is the most basic function of functional programming. In this way, function languages can support closure. Let's look at an example of an Erlang anonymous function.
Outer (c)->
Inner = fun (a, B)-> A + B + C end,
Inner (2, 3 ).
This code first defines a naming function outer, and then defines an anonymous function within the outer function. We can see that this anonymous function is defined by the keyword fun. As mentioned above, function-based programming functions are equivalent to class instance objects for Object-Oriented Programming, and anonymous functions are also similar to class instances, we can assign this anonymous function to a variable inner, and then we can call this variable as a function, for example, inner (2, 3 ).
Fun is the keyword used by Erlang to define anonymous functions. This keyword is very important. The usage of fun's anonymous function definition is not very complex, and it is similar to the naming function definition.
The definition of function branches is similar, but end is used instead of end. "And fun only needs to be written once. It does not need to be written to every branch like a naming function. For example,
Myfunction = fun (0)-> 0;
(Number)-> Number * 10 + 1 end,
Myfunction (3 ),
Function as a variable
Anonymous functions can be assigned to variables as objects, and naming functions can also be assigned to variables. The specific usage still requires the help of important fun keywords. For example,
Myfunction = fun outer/1
You can assign the outer function defined above to the myfunction variable. The following/0 indicates that this outer function has only one parameter. Erlang allows the definition of multiple functions with the same name. As long as the number of parameters is different, it is a different function.
As we can see, any function can be used as a variable, or transmitted as a parameter or return value. These variables can also be called as functions at any time, so they are dynamic.
Dynamic function call
Erlang has an apply function that can dynamically call a function variable.
The basic usage is apply (function variable, function parameter list ). For example, the above myfunciton function variable can be called in this way, apply (myfunction, [5]).
Can we obtain a function variable based on a string as the function name? In this way, we can dynamically call a function based on a string.
In Erlang, it is very easy to do this. As mentioned above, once the function name is defined, it is naturally fixed, which is similar to the constant name, which belongs to an immutable atom (atom ). All atom can be converted to a string or a string. The strings in Erlang are essentially lists. The conversion between the string and atom is achieved through list_to_atom and atom_to_list.
So we can obtain myfunciton: myfunction = list_to_atom ("outer") in this way ")
If the outer function is defined, myfucntion is equivalent to the outer function. If the outer function is not defined, list_to_atom ("outer") will generate a new atom called outer, myfucntion is equal to the newly generated atom.
To generate an existing atom, we need to call the list_to_existing_atom conversion function. Instead of generating a new atom, this function returns an existing atom.
Tuple as a data member set
As mentioned above, functional programming has no member variables for object-oriented programming. This is a limitation.
The tuple type of Erlang can overcome this restriction to some extent. Tuple can accommodate member variables to a certain extent.
The object-oriented class definition is actually a set of data and functions, but the members of the set have a this pointer associated and can be found with each other.
The tuple type of Erlang is the set of data and can naturally play the role of member variables, such as {member1, member2 }.
The reader may say that Erlang functions can also be used as variables or placed in tuple, for example, {memer1, member2, funtion1, function2 }. Isn't that the same as object-oriented programming?
Unfortunately, this is not worth the candle. Because function programming does not support the inherent this pointer of object-oriented programming, and naturally does not support the inherent polymorphism and inheritance, it is hard to combine data and functions into a tuple, there is no benefit at all, and the flexibility of functions as instance objects is also lost.
Therefore, the best practice of functional programming (best practice) should be: tuple is used to hold Member Data and function operation tuple. The combination of tuple definition and Function Definition forms a loose data structure, which is similar to object-oriented class definition. The data structure of the tuple + function has the characteristics of polymorphism, because the function itself can be replaced as a variable; but it does not have the inheritance feature, because it does not have the internal support of the this pointer.
Because tuple plays an important role in data type construction, Erlang introduces a macro definition called record, which can name the subscript position of the array of tuple. For example, the first element is called address, and the second element is called zipcode, so that programmers can access the elements in the tuple by using these names without accessing them by array subscript.
The specific usage of tuple and record is still complex. Due to its limited space, this chapter does not describe it and only introduces some key principles.
Others
Erlang has other syntax features and details. Interested readers can go to the Erlang website (www.erlang.org) for research.