Aktif Modeller
Etkin desenler giriş verilerini alt bölümlere ayıracak adlandırılmış bölümler tanımlamanıza olanak tanır, böylece bu adları ayrımcı birleşimde olduğu gibi desen eşleştirme ifadesinde kullanabilirsiniz. Verileri her bölüm için özelleştirilmiş bir şekilde ayrıştırmak için etkin desenler kullanabilirsiniz.
Sözdizimi
// Active pattern of one choice.
let (|identifier|) [arguments] valueToMatch = expression
// Active Pattern with multiple choices.
// Uses a FSharp.Core.Choice<_,...,_> based on the number of case names. In F#, the limitation n <= 7 applies.
let (|identifier1|identifier2|...|) valueToMatch = expression
// Partial active pattern definition.
// Can use FSharp.Core.option<_>, FSharp.Core.voption<_> or bool to represent if the type is satisfied at the call site.
let (|identifier|_|) [arguments] valueToMatch = expression
Açıklamalar
Önceki söz diziminde tanımlayıcılar,
Örneğin, bir argüman içeren aşağıdaki aktif deseni göz önünde bulundurun.
let (|Even|Odd|) input = if input % 2 = 0 then Even else Odd
Aşağıdaki örnekte olduğu gibi bir desen eşleştirme ifadesinde etkin deseni kullanabilirsiniz.
let TestNumber input =
match input with
| Even -> printfn "%d is even" input
| Odd -> printfn "%d is odd" input
TestNumber 7
TestNumber 11
TestNumber 32
Bu programın çıktısı aşağıdaki gibidir:
7 is odd
11 is odd
32 is even
Etkin desenlerin bir diğer kullanımı da veri türlerini, örneğin aynı temel alınan verilerin çeşitli olası gösterimlerine sahip olması gibi çeşitli yollarla ayrıştırmaktır. Örneğin, bir Color
nesnesi RGB gösterimine veya HSB gösterimine bölünebilir.
open System.Drawing
let (|RGB|) (col : System.Drawing.Color) =
( col.R, col.G, col.B )
let (|HSB|) (col : System.Drawing.Color) =
( col.GetHue(), col.GetSaturation(), col.GetBrightness() )
let printRGB (col: System.Drawing.Color) =
match col with
| RGB(r, g, b) -> printfn " Red: %d Green: %d Blue: %d" r g b
let printHSB (col: System.Drawing.Color) =
match col with
| HSB(h, s, b) -> printfn " Hue: %f Saturation: %f Brightness: %f" h s b
let printAll col colorString =
printfn "%s" colorString
printRGB col
printHSB col
printAll Color.Red "Red"
printAll Color.Black "Black"
printAll Color.White "White"
printAll Color.Gray "Gray"
printAll Color.BlanchedAlmond "BlanchedAlmond"
Yukarıdaki programın çıkışı aşağıdaki gibidir:
Red
Red: 255 Green: 0 Blue: 0
Hue: 360.000000 Saturation: 1.000000 Brightness: 0.500000
Black
Red: 0 Green: 0 Blue: 0
Hue: 0.000000 Saturation: 0.000000 Brightness: 0.000000
White
Red: 255 Green: 255 Blue: 255
Hue: 0.000000 Saturation: 0.000000 Brightness: 1.000000
Gray
Red: 128 Green: 128 Blue: 128
Hue: 0.000000 Saturation: 0.000000 Brightness: 0.501961
BlanchedAlmond
Red: 255 Green: 235 Blue: 205
Hue: 36.000000 Saturation: 1.000000 Brightness: 0.901961
Birlikte, etkin desenleri kullanmanın bu iki yolu, verileri yalnızca uygun biçimde bölümlemenize ve ayrıştırmanıza ve uygun veriler üzerinde hesaplama için en uygun biçimde uygun hesaplamaları gerçekleştirmenize olanak tanır.
Sonuçta elde edilen desen eşleştirme ifadeleri, verilerin çok okunabilir ve karmaşık olabilecek dallanma ve veri çözümleme kodunu büyük ölçüde basitleştiren kullanışlı bir şekilde yazılabilmesini sağlar.
Kısmi Etkin Desenler
Bazen, giriş alanının yalnızca bir bölümünü bölümlemeniz gerekir. Bu durumda, her biri bazı girişlerle eşleşen ancak diğer girişlerle eşleşmeyen kısmi desenler kümesi yazarsınız. Her zaman değer üretmeyen etkin desenler, kısmi etkin desenler
let (|Integer|_|) (str: string) =
let mutable intvalue = 0
if System.Int32.TryParse(str, &intvalue) then Some(intvalue)
else None
let (|Float|_|) (str: string) =
let mutable floatvalue = 0.0
if System.Double.TryParse(str, &floatvalue) then Some(floatvalue)
else None
let parseNumeric str =
match str with
| Integer i -> printfn "%d : Integer" i
| Float f -> printfn "%f : Floating point" f
| _ -> printfn "%s : Not matched." str
parseNumeric "1.1"
parseNumeric "0"
parseNumeric "0.0"
parseNumeric "10"
parseNumeric "Something else"
Önceki örneğin çıktısı aşağıdaki gibidir:
1.100000 : Floating point
0 : Integer
0.000000 : Floating point
10 : Integer
Something else : Not matched.
Kısmi etkin desenler kullanılırken, bazen tek tek seçenekler ayrı veya birbirini dışlayan olabilir, ancak böyle olması gerekmez. Aşağıdaki örnekte, bazı sayılar hem kare hem de küp olduğundan, 64 gibi, Kare deseni ve Küp deseni ayrık değildir. Aşağıdaki program, Kare ve Küp desenlerini birleştirmek için AND desenini kullanır. Hem kare hem de küp olan 1000'e kadar olan tüm tamsayıların yanı sıra yalnızca küp olan tamsayıları yazdırır.
let err = 1.e-10
let isNearlyIntegral (x:float) = abs (x - round(x)) < err
let (|Square|_|) (x : int) =
if isNearlyIntegral (sqrt (float x)) then Some(x)
else None
let (|Cube|_|) (x : int) =
if isNearlyIntegral ((float x) ** ( 1.0 / 3.0)) then Some(x)
else None
let findSquareCubes x =
match x with
| Cube x & Square _ -> printfn "%d is a cube and a square" x
| Cube x -> printfn "%d is a cube" x
| _ -> ()
[ 1 .. 1000 ] |> List.iter (fun elem -> findSquareCubes elem)
Çıkış aşağıdaki gibidir:
1 is a cube and a square
8 is a cube
27 is a cube
64 is a cube and a square
125 is a cube
216 is a cube
343 is a cube
512 is a cube
729 is a cube and a square
1000 is a cube
Parametreli Etkin Desenler
Etkin desenler, eşleştirilen öğe için her zaman en az bir bağımsız değişken alır, ancak ek bağımsız değişkenler de alabilirler; bu durumda ad parametreli etkin desen uygulanır. Ek bağımsız değişkenler, genel bir desenin özelleştirilebilir olmasını sağlar. Örneğin, dizeleri ayrıştırmak için normal ifadeler kullanan etkin desenler genellikle normal ifadeyi, önceki kod örneğinde tanımlanan kısmi etkin desen Integer
kullanan aşağıdaki kodda olduğu gibi ek bir parametre olarak içerir. Bu örnekte, genel ParseRegex etkin desenini özelleştirmek için çeşitli tarih biçimleri için normal ifadeler kullanan dizeler verilmiştir. Tamsayı etkin deseni, eşleşen dizeleri DateTime oluşturucusuna geçirilebilen tamsayılara dönüştürmek için kullanılır.
open System.Text.RegularExpressions
// ParseRegex parses a regular expression and returns a list of the strings that match each group in
// the regular expression.
// List.tail is called to eliminate the first element in the list, which is the full matched expression,
// since only the matches for each group are wanted.
let (|ParseRegex|_|) regex str =
let m = Regex(regex).Match(str)
if m.Success
then Some (List.tail [ for x in m.Groups -> x.Value ])
else None
// Three different date formats are demonstrated here. The first matches two-
// digit dates and the second matches full dates. This code assumes that if a two-digit
// date is provided, it is an abbreviation, not a year in the first century.
let parseDate str =
match str with
| ParseRegex "(\d{1,2})/(\d{1,2})/(\d{1,2})$" [Integer m; Integer d; Integer y]
-> new System.DateTime(y + 2000, m, d)
| ParseRegex "(\d{1,2})/(\d{1,2})/(\d{3,4})" [Integer m; Integer d; Integer y]
-> new System.DateTime(y, m, d)
| ParseRegex "(\d{1,4})-(\d{1,2})-(\d{1,2})" [Integer y; Integer m; Integer d]
-> new System.DateTime(y, m, d)
| _ -> new System.DateTime()
let dt1 = parseDate "12/22/08"
let dt2 = parseDate "1/1/2009"
let dt3 = parseDate "2008-1-15"
let dt4 = parseDate "1995-12-28"
printfn "%s %s %s %s" (dt1.ToString()) (dt2.ToString()) (dt3.ToString()) (dt4.ToString())
Önceki kodun çıkışı aşağıdaki gibidir:
12/22/2008 12:00:00 AM 1/1/2009 12:00:00 AM 1/15/2008 12:00:00 AM 12/28/1995 12:00:00 AM
Etkin desenler yalnızca desen eşleştirme ifadeleriyle sınırlı değildir, bunları let-binding'lerde de kullanabilirsiniz.
let (|Default|) onNone value =
match value with
| None -> onNone
| Some e -> e
let greet (Default "random citizen" name) =
printfn "Hello, %s!" name
greet None
greet (Some "George")
Önceki kodun çıkışı aşağıdaki gibidir:
Hello, random citizen!
Hello, George!
Ancak yalnızca tek durumlu etkin desenlerin parametrelendirilebileceğini unutmayın.
// A single-case partial active pattern can be parameterized
let (| Foo|_|) s x = if x = s then Some Foo else None
// A multi-case active patterns cannot be parameterized
// let (| Even|Odd|Special |) (s: int) (x: int) = if x = s then Special elif x % 2 = 0 then Even else Odd
Kısmi Etkin Desenler için Dönüş Türü
Kısmi etkin desenler, eşleşmeyi göstermek için Some ()
döndürür, diğer durumlarda None
döndürür.
Şu eşleşmeyi göz önünde bulundurun:
match key with
| CaseInsensitive "foo" -> ...
| CaseInsensitive "bar" -> ...
Kısmi etkin deseni şöyle olabilir:
let (|CaseInsensitive|_|) (pattern: string) (value: string) =
if String.Equals(value, pattern, StringComparison.OrdinalIgnoreCase) then
Some ()
else
None
F# 9'dan başlayarak bu tür desenler de bool
döndürebilir:
let (|CaseInsensitive|_|) (pattern: string) (value: string) =
String.Equals(value, pattern, StringComparison.OrdinalIgnoreCase)
Kısmi Etkin Desenler için Yapı Gösterimleri
Varsayılan olarak, kısmi bir etkin desen option
döndürürse, başarılı bir eşleşmede Some
değeri için bir ayrım yapılır. Bunu önlemek için, değer seçeneğini dönüş değeri olarak Struct
özniteliği ile kullanabilirsiniz.
open System
[<return: Struct>]
let (|Int|_|) str =
match Int32.TryParse(str) with
| (true, n) -> ValueSome n
| _ -> ValueNone
Öznitelik belirtilmelidir, çünkü bir yapı dönüşünün kullanıldığı, sadece dönüş türünü ValueOption
olarak değiştirmekle çıkarılamaz. Daha fazla bilgi için bkz. RFC FS-1039