Introduction to Active Patterns
Introduction to Active Patterns
Fundamentals of Active Patterns
Introduction
An active pattern is a special name that either you can use as a function or you can involve in pattern matching.
The primary formula to create an active pattern for a function is:
let (|function-name|) [ argument(s) ] = body
You start with the let keyword followed by parentheses. In the parentheses, add two pipes. Inside the pipes, add a name for a function. By tradition, the name inside the pipes uses the convention for types. Here is an example:
let (|Show|) . . .
Outside the parentheses, if the function will not take an argument, type some empty parentheses or an underscore. This is followed by the normal = sign of a regular function. As you know already, the section after = is the body of the function. If you are not planning to do anything significant, you can use () as the whole body of the function. Here is an example:
let (|Show|) _ = ()
Using a Functional Active Pattern
The function as we have just created it is primarily a regular function like any other. This means that you can use it when necessary. To call the function, include it in its pipes and parentheses. If the function didn’t take any argument, make sure you add empty parentheses to it. Here is an example:
let (|Show|) _ = ()
(|Show|)()
This would produce:
Press any key to close this window . . .
You can make the function behave like any other. For example, in its body, you can display something. Here is an example:
let (|Show|) _ = printfn "The wonderful world of functional programming" (|Show|)()
This would produce:
The wonderful world of functional programming Press any key to close this window . . .
A Functional Active Pattern with Argument
You can pass an argument to the function. This is done just after the parentheses of the function name. Here is an example:
let (|Calculate|) x = ...
Of course, you can specify the data type of the argument. In this case, you must include it in parentheses, with its type, both separated by a colon. This would be done as follows:
let (|Calculate|) (x : float) = ...
In the body of the function, use the argument as you see fit. Here is an example for a function used to calculate the area of a circle while its argument represents the radius of the circle:
let (|Calculate|) x = x * x * 3.14
You can then call the function and pass the desired value to it. Here is an example:
let (|Calculate|) x = x * x * 3.14 let area = (|Calculate|) 25.50 printfn "Area of Circle: %0.04f" area
This would produce:
Area of Circle: 2041.7850 Press any key to close this window . . .
Otherwise, you can declare a variable for the argument that you will pass to the function. Here is an example:
let (|Calculate|) x = x * x * 3.14 let radius = 48.79 let area = (|Calculate|) radius printfn "Circle Characteristics" printfn "----------------------" printfn "Radius: %0.02f" radius printfn "Area: %0.04f" area
This would produce:
Circle Characteristics ---------------------- Radius: 48.79 Area: 7474.6573 Press any key to close this window . . .
Passing Arguments to a Functional Active Pattern
You can pass as many arguments as you want, and you can use the arguments any way you want. Here is an example:
let (|Calculate|) x y = x * y let length = 48.79 let height = 33.63 let area = (|Calculate|) length height printfn "Rectangle Characteristics" printfn "----------------------" printfn "Length: %0.02f" length printfn "Height: %0.02f" height printfn "Area: %0.04f" area
This would produce:
Rectangle Characteristics ---------------------- Length: 48.79 Height: 33.63 Area: 1640.8077 Press any key to close this window . . .
Of course, you can do more than a short simple calculation in the function.
Matching an Active Pattern
An active pattern can be used as the expression of a matching pattern. Here is an example:
let (|Show|) _ = () match (|Show|) with
If the active pattern was created as a function, the matching pattern should have only one option. If the function of the active pattern didn’t take an argument, you can use anything as the option of the matching pattern. Here is an example:
let (|Show|) _ = ()
match (|Show|) with
| anything ->
Since that option doesn’t hold any value, it cannot be used; it is just an empty insignificant value. Therefore, as an alternative, you can use the underscore. Here is an example:
let (|Show|) _ = ()
match (|Show|) with
| _ ->
In the body of the option, you can call the function if you want. In this case, you must use the whole name, including its parentheses and pipes. If the function didn’t take an argument, add some empty parentheses to it. Here is an example:
let (|Show|) _ = ()
match (|Show|) with
| _ -> (|Show|)()
As seen earlier, you can put any statement of your choice in the body of the function, such as displaying something. Here is an example:
let (|Show|) _ = printfn "The wonderful world of functional programming"
match (|Show|) with
| _ -> (|Show|)()
This would produce:
The wonderful world of functional programming Press any key to close this window . . .
You can also use the option in the pattern matching any way you want, such as displaying a value. Here is an example:
let (|Show|) _ = () match (|Show|) with | _ -> printfn "This is the wonderful world of F# programming"
This would produce:
This is the wonderful world of F# programming Press any key to close this window . . .
In most cases, you create a functional active pattern because you want to perform an operation. In this case, you should pass an argument to the function. In the body of the function, use the argument as you see fit. Here is an example we saw earlier:
let (|Calculate|) x = x * x * 3.14
As we saw earlier, you can then call the function and pass the desired value to it. You can also call the function as the pattern to match and pass the necessary value for its argument. Here is an example:
let (|Calculate|) x = x * x * 3.14
match (|Calculate|) 25.50 with
| _ -> printfn "Area of Circle: %0.04f" ((|Calculate|) 25.50)
Or you can declare a variable for the argument, initialize that variable, and pass it to the function. Here is an example:
let (|Calculate|) x = x * x * 3.14 let radius = 48.79 let area = (|Calculate|) radius match area with | _ -> printfn "Area of Circle: %0.04f" area
Of course, you can create more than one statement in the pattern matching. Here is an example:
let (|Calculate|) x = x * x * 3.14
let radius = 48.79
let area = (|Calculate|) radius
match area with
| _ -> printfn "Circle Characteristics"
printfn "----------------------"
printfn "Radius: %0.02f" radius
printfn "Area: %0.04f" area
This would produce:
Circle Characteristics ---------------------- Radius: 48.79 Area: 7474.6573 Press any key to close this window . . .
Remember that you can pass as many arguments as you want to the function. Here is an example we saw earlier:
let (|Calculate|) x y = x * y let length = 48.79 let height = 33.63 let area = (|Calculate|) length height match (|Calculate|) length height with | _ -> printfn "Rectangle Characteristics" printfn "----------------------" printfn "Length: %0.02f" length printfn "Height: %0.02f" height printfn "Area: %0.04f" area
You can also call the function as the option of the pattern matching. If the function was passed an argument, you can pass it any name as argument. Here is an example:
let (|Calculate|) x = x * x * 3.14
let radius = 48.88
match radius with
| Calculate something -> printfn "Area: %0.04f" something
Partitioning an Active Pattern
Introduction
As you can seen and we will see, there are various ways active patterns are used. We saw that you can create a name for a function, define it, and use it like any normal function. Another option is to create various names (one to seven) that you can use in a matching pattern. The formula to follow is:
let (|name1|name2|name_n|) [ argument(s) ] = body
In the parentheses, create one to seven names. Each name must be included inside pipes. Here is an example:
let (|Square|Circle|) . . .
This time too, the argument(s) is (are) not (a) requirement but you should add at least one. The argument is used to let you choose what name will be used and when. In this case, the argument should be an integer. In the body of the active pattern, you can create a conditional statement or a matching pattern to associate (select) each option based on a certain value. Here is an example:
let (|Square|Circle|) x = match x with | 1 -> Square | _ -> Circle // if x = 1 then Square // else Circle
You can then use the active pattern in another statement. For example, you can define a function that will take action based on what name is valid. Here is an example:
let (|Square|Circle|) x = match x with | 1 -> Square | _ -> Circle let calculateArea shape value = match shape with | Square -> value * value | Circle -> value * value * 3.14 let area = calculateArea 1 26.79 printfn "Area: %0.04f" area
This would produce:
Area: 717.7041 Press any key to close this window . . .
Partial Active Patterns
Another way you can use an active pattern is to validate a choice while not caring for another. This is referred to as a partial active pattern. The formula to create it is:
let (|name|_|) [ argument(s) ] = body
Add three pipes in the parentheses. In the left section, add a name. In the right section, use an underscore. This time also, you should add an argument. The argument will be used to validate the lone active pattern. Here is an example:
let (|Sphere|_|) x = match x with | x -> Some(x) let calculateArea volume value = match volume with | Sphere -> value * value * 4.00 * 3.14 | _ -> 0.00 let area = calculateArea () 26.79 printfn "Area: %0.04f" area
This would produce:
Area: 9014.3635 Press any key to close this window . . .
|
|||
Previous | Copyright © 2024, FunctionX | Monday 14 February 2016 | Next |
|