As in many other functional programming languages, pattern matching (pattern matching) is supported in F #. Those who are not familiar with the OP can imagine it as a
much improved switch.
Go to the code.
let rec factorial n =
match n
with| 0 | 1 -> 1| _
-> n * factorial
( n -
1 )')
This code defines a recursive factorial function. As you can see, it is very similar to switch.
Couples, threes, etc.
let ( +
) vec multiplier =
match vec
with x, y
->x * multiplier, y * multiplier
Here the element is matched with a pair of values (touple). Accordingly, x takes the value of the first element of the pair, y - the second. Note that x and y are new value descriptors. If earlier in the program another value was described with the name x, then it will be hidden again by the end of the branch. In order not to introduce a new description, you must use the underscore character, as was done in the first code example. It can occur in a sample as many times as desired and is essentially a placeholder.
Lists
let rec sum =
function| [ ] -> 0| head
:: tail
->head + sum tail
This abbreviated entry is equivalent to
let rec sum value =
match value
with| [ ] -> 0| head
:: tail
->head + sum tail
The only difference is that a descriptor with the name value is not entered.
The first pattern, [], corresponds to an empty list. The second sample describes the list as two parts, the head (the first element) and the tail (a list of all the other elements). Names in place of head and tail can be arbitrarily chosen. Their field of view is to the end of the branch.
You can separate multiple items from the beginning of the list. el1 :: _ :: el3 :: tail
Several meanings
When comparing samples, one can not only break a pair into its components, but also work with a pair of values.
match host port
with| “Microsoft.com” ,
80 -> Allow
| “Warezzz.ws” ,
80 -> Deny
| _
-> Forward
Pattern matching can be used to determine types (dynamic).
match obj
with| : ? String
as str
-> "String!"| : ? Int32
as integer
-> "Int!"| _
-> "What is it?"Additional terms
let rec hasNegative =
function| [ ] -> false| head
:: tail
when head <
0 -> true| _
:: tail
-> hasNegative tail
I want to note that the comparison of samples occurs consistently and therefore if one of the samples came up, then the subsequent ones are no longer compared. Due to this, it is not necessary to write when head> = 0 in the last branch.
Active Patterns
This feature is probably unique to F #. We are given the opportunity to define our own matching methods. Consider a simple example.
let ( | Odd
| Even
| ) =
function| x
when x%
2 =
0 -> Even
| _
-> Odd
match 10 with| Odd
-> "oO"| Even
-> ":)"The matching method is essentially a special defined function. She does not have a name, but a pair of samples is determined at once - Odd and Even.
In addition to the simple choice, you can organize arbitrary. Partial matching methods are used for this.
let ( | Prime
| _
| ) value =
let composite = List
. exists
( fun n
-> value% n =
0 ) [ 2 . . int ( Math
. Sqrt
( value
) ) ]Some
( value
)else None
match n
with| Prime x
-> sprintf
"input% d is prime!" X
| x
-> sprintf
"this value is not special"This was a fairly simple example. Let's look more complicated.
type StackItem =
| Int
of int32
| String
of string
let ( | Add
| _
| ) =
function| OpCode
. Add, Int
( a
) :: Int
( b
) :: stackRest
->Some
( Int
( a + b
) :: stackRest
)| OpCode
. Add, String
( a
) :: String
( b
) :: stackRest
->Some
( String
( a + b
) :: stackRest
)| OpCode
. Add, _
->InvalidProgramException
( "invalid add args" ) | > raise
| _
-> None
let ( | Sub
| _
| ) =
function| OpCode
. Add, Int
( a
) :: Int
( b
) :: stackRest
->Some
( Int
( a - b
) :: stackRest
)| OpCode
. Add, _
->InvalidProgramException
( "invalid sub args" ) | > raise
| _
-> None
let compute opcode currentStack =
match opcode, currentStack
with| Add
( newStack
) | Sub
( newStack
) -> newStack
| _
-> InvalidProgramException
( sprintf
"invalid opcode% A" opcode
)In the partial matching method, a special type of option (analogue of Nullable) is used, described in the following way:
type Option 'a =
| None
| Some
of 'a
Accordingly, to indicate that the object does not match the pattern, None is returned. In this case, we also return the result of applying the pattern as a parameter Some and then use it (newStack).
Results
As you can see, the comparison of samples is quite a useful thing. They are almost the only reason why I choose F # instead of C # (it is very convenient to use them during translation, see the example code with the stack machine). Many use cases, compact code, extensibility due to its matching methods - all this makes pattern matching in F # very, very attractive.
PS I apologize for the fact that not all examples of the code are the correct quotes. This is a malicious joke played either habraparser, or a means of highlighting the code.