Etkin Desenler
Etkin desenler, giriş verilerini alt bölümlere ayıran adlandırılmış bölümler tanımlamanıza olanak sağlar, böylece bu adları aynı ayrımlı bir birliktelik için olduğu gibi bir desen eşleştirme ifadesinde kullanabilirsiniz. Her bölüm için verileri özelleştirilmiş bir şekilde bölmek için etkin desenleri kullanabilirsiniz.
Syntax
// 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.
// Uses a FSharp.Core.option<_> to represent if the type is satisfied at the call site.
let (|identifier|_|) [arguments] valueToMatch = expression
Açıklamalar
Önceki söz dizimsinde tanımlayıcılar, bağımsız değişkenlerle temsil edilen giriş verisi bölümlerinin adları veya diğer bir deyişle, bağımsız değişkenlerin tüm değerleri kümesinin alt kümeleri için adlardır. Etkin bir desen tanımında en fazla yedi bölüm olabilir. ifadesi, verilerin hangi formda olduğunu açıklar. Bağımsız değişken olarak verilen değerlerin hangi adlandırılmış bölümlere ait olduğunu belirlemek için kuralları tanımlamak için etkin bir desen tanımı kullanabilirsiniz. (| ve |) sembolleri muz klipleri olarak adlandırılır ve bu tür bir let bağlaması tarafından oluşturulan işlev etkin tanıyıcı olarak adlandırılır.
Örneğin, bir bağımsız değişken ile aşağıdaki etkin deseni göz önünde bulundurarak.
let (|Even|Odd|) input = if input % 2 = 0 then Even else Odd
Etkin deseni, aşağıdaki örnekte olduğu gibi desen eşleştirme ifadesinde 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 alanı da veri türlerini, örneğin aynı temel alınan verilerin çeşitli olası gösterimlerine sahip olması gibi birden çok şekilde dekler. Örneğin, bir Color nesne RGB gösterimine veya HSB gösterimine göre ya da bir HSB gösterimine göre yalıtabilirsiniz.
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 çıktısı 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 forma bölümleme ve bölme ve hesaplama için en uygun formda uygun veriler üzerinde uygun hesaplamaları gerçekleştirmeye olanak sağlar.
Sonuçta elde edilen desen eşleştirme ifadeleri, verilerin çok okunabilir, karmaşık olabilecek dallara alma ve veri analizi kodunu büyük ölçüde kolaylaştıran kullanışlı bir şekilde yazılabilir.
Kısmi Etkin Desenler
Bazen giriş alanı yalnızca bir bölümünü bölümlemelisiniz. Bu durumda, her biri bazı girişlere eş olan ancak diğer girişler ile eşleşmeyen bir kısmi desen kümesi yazarsınız. Her zaman değer üretmeden etkin desenlere kısmi etkin desenler denir; bir seçenek türü olan bir dönüş değerine sahipler. Kısmi etkin bir desen tanımlamak için, muz kliplerinin içindeki desen listesinin sonunda joker karakter ( _ ) kullanırsiniz. Aşağıdaki kod kısmi etkin desenin kullanımını göstermektedir.
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ırken, bazen tek tek seçimler ayrık veya birbirini dışlar, ancak bu seçeneklere gerek olmaz. Aşağıdaki örnekte, bazı sayılar hem kareler hem de 64 gibi küpler olduğundan Square ve Cube 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 ve yalnızca küp olan 1000'e kadar olan tüm 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)
Çıktı aşağıdaki şekilde olacaktır:
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ştirilmiş öğe için her zaman en az bir bağımsız değişken alır, ancak ad parametreli etkin desenin geçerli olduğu ek bağımsız değişkenler de alır. Ek bağımsız değişkenler, genel bir desenin özelleşmiş olmasına olanak 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 deseni de kullanan aşağıdaki kodda olduğu gibi ek bir parametre Integer olarak içerir. Bu örnekte, çeşitli tarih biçimleri için normal ifadeler kullanan dizeler, genel ParseRegex etkin desenini özelleştirmek için verilir. Tamsayı etkin deseni, eş zamanlı dizeleri DateTime oluşturucusuza geçirilecek 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 çıktısı 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 ifadeleri ile sınırlı değildir, bunları let bağlamaları üzerinde 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 çıktısı aşağıdaki gibidir:
Hello, random citizen!
Hello, George!
Ancak, yalnızca tek büyük/büyük/büyük harf etkin desenler parametreli hale getirildi.
// 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 Yapı Gösterimleri
Varsayılan olarak, kısmi etkin desenler option başarılı bir eşleşmede değer için ayırmayı Some içeren bir değer verir. Alternatif olarak, özniteliğini kullanarak bir değer seçeneğini dönüş değeri olarak Struct kullanabilirsiniz:
open System
[<return: Struct>]
let (|Int|_|) str =
match Int32.TryParse(str) with
| (true, n) -> ValueSome n
| _ -> ValueNone
Bir yapı dönüş kullanımının yalnızca dönüş türü olarak değiştirilmesinden kaynaklanmaz, çünkü özniteliği ValueOption belirtilmelidir. Daha fazla bilgi için bkz. RFC FS-1039.