Introduction to functional programming paradigm
F # It mainly supports three programming paradigms: functional programming (functional programming, FP) and imperative programming (imperative ).
Programming) and object-oriented (object-oriented, OO) programming. Looking back at their history, FP is the earliest paradigm. The first FP language is IPL,
It was born in 1955, about a year before FORTRAN. The second FP language is LISP, produced in 1958, a year earlier than Cobol. Both fortan and COBOL are imperative.
Programming languages, their rapid success in science and commerce have made imperative programming dominant over the past 30 years. The object-oriented programming, which was developed in the 1970 s, has become the most popular
Programming Paradigm. There is a saying that "Jiangshan has a language and has been around for decades ".
Although the strong FP language (SML, ocaml, Haskell, and clean) and FP-like language (APL and lisp were the most successful in the real world) in 1950
With the continuous development of the generation, FP is still in the academic "Ivory Tower", while imperative programming and object-oriented programming take the lead in the business field and enterprise applications respectively. Today, the potential of FP is recognized
Knowledge-it is used to solve more complex problems (of course, a simpler problem is also difficult ).
Pure FP treats the program as a set of functions that accept parameters and return values, and does not allow side effects (
Effect, that is, changing the State), uses recursion instead of repeating iteration. Functions in FP are similar to functions in mathematics. They do not change the state of the program. For example, once a value is assigned
An identifier, it will not change, the function does not change the value of the parameter, the return value is a new value.
The mathematical foundation of FP makes it very elegant, and FP programs tend to look simple and beautiful. However, its stateless and recursive nature makes it easier to process many common programming tasks without other programming paradigms. But for F #, this is not a problem. One of its advantages is that it integrates Multiple Programming paradigms, allowing developers to adopt the best paradigm as needed.
For more information about FP, read this article:
Why functional programming matters: http://www.md.chalmers.se /~ Rjmh/papers/whyfp.pdf
(Chinese version: http://www.nirvanastudio.org/wp-content/uploads/2006/08/why functional programming matters.html ).
Function programming in F #
From now on, I will introduce the main FP-related language structures in F # one by one.
Identifier)
In F #, we use the identifier to name the value so that it can be referenced in the subsequent program. Use the keyword let to define the identifier, such:
Letx = 42
This looks like the value assignment statement in imperative programming languages. The two have key differences. In pure FP, once a value is assigned to an identifier, it cannot be changed. This is also called an identifier rather than a variable.
(Variable. In addition, in some conditions, we can redefine the identifier. In the imperative programming paradigm of F #, the value of the identifier can be modified under some conditions.
An identifier can also be used to reference a function. In F #, a function is essentially a value. That is to say, F # does not have the concept of true function names and parameter names. They are all identifiers. The method for defining a function is similar to that for defining a value, but an additional identifier is provided to indicate the parameter:
Letaddxy = x + y
There are three identifiers. Add indicates the function name, and X and Y indicate its parameters.
Keywords and reserved words
Keywords refer to some of the markup in the language, which are reserved by the compiler for special use. In F #, it cannot be used as an identifier or type name ("definition type" will be discussed later "). They are:
abstractandasasrassertbeginclassdefaultdelegatedodone
downcastdowntoelifelseendexceptionexternfalsefinallyfor
funfunctionifininheritinlineinterfaceinternallandlazylet
lorlsrlxormatchmembermodmodulemutablenamespacenewnull
ofopenoroverrideprivatepublicrecreturnsigstaticstruct
thentotruetrytypeupcastusevalvoidwhenwhilewithyield
Reserved words refer to words that are not keywords yet, but are reserved by F # for future use. They can be used to define identifiers or type names, but the compiler reports a warning. If you care about the compatibility between programs and future compilers, it is best not to use them. They are:
atomicbreakcheckedcomponentconstconstraintconstructorcontinue
eagereventexternalfixedfunctorglobalincludemethodmixin
objectparallelprocessprotectedpuresealedtraitvirtualvolatile
Literals)
The text value represents a constant value, which is useful when constructing a code block. F # provides a rich set of text values. Similar to C #, these text values include common strings, characters, Boolean values, integer values, and floating point numbers. For more information, see F.
Like C #, string constants in F # can be expressed in two ways. 1. Regular string (regular
String), which can contain escape characters; the second is a verbatim string (verbatim
String), where (") is considered as a regular character, and two double quotation marks are used as escape characters of double quotation marks. The following simple example demonstrates the common literal constant representation:
Letmessage = "helloworld" R "n! "// Regular string
Letdir = @ "C:" FS "FP" // verbatim string
Letbytes = "bytes" B // byte array
Letxa = 0 xffy // sbyte, in hexadecimal notation
Letxb = 0o777un // unsignednative-sizedinteger, in octal format
Letprintx = printfn "% A" x
Letmain () =
Printmessage;
Printdir;
Printbytes;
Printxa;
Printxb;
Main ()
The printf function uses the F # reflection mechanism and. net tostring method to parse the "% A" mode. It is applicable to any type of value. You can also use the print_any and print_to_string functions in F # To complete similar functions.
Values and functions)
In F #, functions are also values, and F #'s syntax for processing them is similar.
letn=10
letaddab=a+b
letaddFour=add4
letresult=addFourn
printfn"result=%i"result
We can see that the syntax for defining value n is similar to that for function add, except that add has two parameters. For add, A +
The value of B is automatically returned. That is to say, in F #, we do not need to explicitly define the return value for the function. For the addfour function, it is defined on the basis of ADD. It only transmits
One parameter, so that different values are returned for different parameters addfour. Consider the function concept in mathematics, f (x, y) = x + y, g (y) = f (4,
Y), actually g (y) = 4 +
Y and G are also functions that receive a parameter. Is it similar in this place? This kind of feature that only transmits some parameters to a function is called the function's curried function ).
Of course, for some functions, it is meaningless to pass some parameters. In this case, all parameters need to be provided forcibly, but the parameters are included and converted to tuple ). The following example cannot be compiled:
letsub(a,b)=a-b
letsubFour=sub4
Two parameters must be provided for sub, such as Sub (4, 5), which is similar to the method call in C.
For the two methods, the former is more flexible and generally takes priority.
If some median values need to be defined during function calculation, we should indent these rows:
lethalfWayab=
letdif=b-a
letmid=dif/2
mid+a
Note that space rather than tab should be used for indentation. If you do not want to press the Space key several times each time, you can set it in vs to automatically convert the Tab character to a space; although there is no limit on the number of indentations, it is generally recommended to use four spaces. You must add the # light command at the beginning of the file.
Scope)
Scope is an important concept in programming languages. It indicates where an identifier or type can be accessed (used. All identifiers, whether they are functions or values, and their scopes start from their declarations,
End the code block from which it resides. For a top-level identifier, once assigned, its value cannot be modified or redefined. Identifiers can be used only after they are defined, which means they are not
Can use its own value.
letdefineMessage()=
letmessage="Helpme"
print_endlinemessage// error
For identifiers defined within a function, their scopes generally reach the end of the function.
However, you can use the let keyword to redefine them. Sometimes this can be very useful. For some functions, the calculation process involves multiple intermediate values, because the values cannot be modified, therefore, we need to define multiple identifiers.
This requires us to maintain the names of these identifiers. In fact, it is unnecessary to use the re-define identifier. But this is not the same as the Identifier value that can be modified. You can even modify the identifier type,
F # ensures type security. The so-called type security basically means that F # Will avoid incorrect operations on the value. For example, we cannot treat integers as strings. This is similar to C.
letchangeType()=
letx=1
letx="changeme"
letx=x+1
print_stringx
In this example, the first and second rows of the function are okay, and the third row is faulty. When re-defining X, the value assigned to it is x + 1, X is a string, and the sum of x and 1 in F # is invalid.
In addition, it is more interesting to redefine identifiers in nested functions.
letprintMessages()=
letmessage="funvalue"
printfn"%s"message;
letinnerFun()=
letmessage="innerfunvalue"
printfn"%s"message
innerFun()
printfn"%s"message
printMessages()
Print result:
funvalue
innerfunvalue
funvalue
The last time is not inner fun value, because innerfun only rebinds the value instead of assigning a value, and its effective range is only within innerfun.
Recursion (recursion)
Recursion is an extremely important concept in programming. It indicates that a function is defined by itself, that is, it calls itself at the definition. In FP, it is often used to express the cycle of imperative programming. Many people think that recursive algorithms are easier to understand than loops.
Use the REC keyword to define recursive functions. Take a look at the following factorial function:
letrecfactorialx=
matchxwith
|xwhenx<0->failwith"valuemustbegreaterthanorequalto0"
|0->1
|x->x*factorial(x-1)
Pattern matching is used here (a great feature of F #). Its C # version is:
publicstaticlongFactorial(intn)
{
if(n<0){thrownewArgumentOutOfRangeException("valuemustbegreaterthanorequalto0");}
if(n==0){return1;}
returnn*Factorial(n-1);
}
Recursion is especially suitable for solving problems such as factorial and Fibonacci series. But be careful when using it. It may write recursion that cannot be terminated.
Anonymous Function)
When defining a function, F # provides the second method: Use the keyword fun. Sometimes we do not need to name a function. This function is called an anonymous function, sometimes called a Lambda function.
Is a new feature of C #3.0. For example, some functions are transmitted to another function as only one parameter, which usually does not need to be named. You can see this example in the "list" section. Besides fun, I
You can also use the function keyword to define anonymous functions. The difference is that the latter can use pattern matching (this article will introduce later) features. See the following example:
letx=(funxy->x+y)12
letx1=(functionx->functiony->x+y)12
letx2=(function(x,y)->x+y)(1,2)
We can give priority to fun because it is more compact. You can see many such examples in the F # class library.
Refer:
Foundations of F # by Robert Pickering
F # specs: http://research.microsoft.com/fsharp/manual/spec2.aspx
Lambda calculus: http://en.wikipedia.org/wiki/Lambda_calculus