Bagikan melalui


Pola Aktif

Pola aktif memungkinkan Anda menentukan partisi bernama yang membagi data input, sehingga Anda dapat menggunakan nama-nama ini dalam ekspresi pencocokan pola seperti yang Anda lakukan untuk union yang terdiskriminasi. Anda dapat menggunakan pola aktif untuk menguraikan data dengan cara yang disesuaikan untuk setiap partisi.

Sintaksis

// 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

Komentar

Dalam sintaks sebelumnya, pengidentifikasi adalah nama untuk partisi data input yang diwakili oleh argumen , atau, dengan kata lain, nama untuk subset dari kumpulan semua nilai argumen. Mungkin ada hingga tujuh partisi dalam definisi pola aktif. Ekspresi menjelaskan formulir untuk menguraikan data. Anda dapat menggunakan definisi pola aktif untuk menentukan aturan untuk menentukan partisi bernama mana yang diberikan nilai sebagai argumen. Simbol (| dan |) disebut sebagai klip pisang dan fungsi yang dibuat oleh jenis pengikatan let ini disebut pengenal aktif.

Sebagai contoh, pertimbangkan pola aktif berikut dengan argumen.

let (|Even|Odd|) input = if input % 2 = 0 then Even else Odd

Anda dapat menggunakan pola aktif dalam ekspresi pencocokan pola, seperti dalam contoh berikut.

let TestNumber input =
   match input with
   | Even -> printfn "%d is even" input
   | Odd -> printfn "%d is odd" input

TestNumber 7
TestNumber 11
TestNumber 32

Output program ini adalah sebagai berikut:

7 is odd
11 is odd
32 is even

Penggunaan lain dari pola aktif adalah mengurai jenis data dengan berbagai cara, seperti ketika data yang mendasar yang sama memiliki berbagai kemungkinan representasi. Misalnya, objek Color dapat diurai menjadi representasi RGB atau representasi HSB.

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"

Output program di atas adalah sebagai berikut:

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

Dalam kombinasi, kedua cara menggunakan pola aktif ini memungkinkan Anda untuk mempartisi dan menguraikan data hanya ke dalam bentuk yang sesuai dan melakukan komputasi yang sesuai pada data yang sesuai dalam bentuk yang paling nyaman untuk komputasi.

Ekspresi pencocokan pola yang dihasilkan memungkinkan data ditulis dengan cara yang mudah dibaca, sangat menyederhanakan pencabangan yang berpotensi kompleks dan kode analisis data.

Pola Aktif Parsial

Terkadang, Anda hanya perlu mempartisi bagian dari ruang input. Dalam hal ini, Anda menulis serangkaian pola parsial yang masing-masing cocok dengan beberapa input tetapi gagal mencocokkan input lain. Pola aktif yang tidak selalu menghasilkan nilai disebut pola aktif parsial; mereka memiliki nilai pengembalian yang merupakan jenis opsi. Untuk menentukan pola aktif parsial, Anda menggunakan karakter kartubebas (_) di akhir daftar pola di dalam klip pisang. Kode berikut mengilustrasikan penggunaan pola aktif parsial.

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"

Output dari contoh sebelumnya adalah sebagai berikut:

1.100000 : Floating point
0 : Integer
0.000000 : Floating point
10 : Integer
Something else : Not matched.

Saat menggunakan pola aktif parsial, kadang-kadang pilihan individu dapat terpisah atau saling eksklusif, tetapi tidak harus begitu. Dalam contoh berikut, pola Persegi dan pola Kubus tidak terputus-putus, karena beberapa angka adalah kotak dan kubus, seperti 64. Program berikut menggunakan pola AND untuk menggabungkan pola Persegi dan Kubus. Ini mencetak semua bilangan bulat hingga 1000 yang merupakan kuadrat dan kubus, serta yang hanya merupakan kubus.

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)

Outputnya adalah sebagai berikut:

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

Pola Aktif Berparameter

Pola aktif selalu mengambil setidaknya satu argumen untuk item yang cocok, tetapi mungkin juga mengambil argumen tambahan, dalam hal ini nama pola aktif berparameter berlaku. Argumen tambahan memungkinkan pola umum untuk dikhususkan. Misalnya, pola aktif yang menggunakan ekspresi reguler untuk mengurai string sering menyertakan ekspresi reguler sebagai parameter tambahan, seperti dalam kode berikut, yang juga menggunakan pola aktif parsial Integer ditentukan dalam contoh kode sebelumnya. Dalam contoh ini, string yang menggunakan ekspresi reguler untuk berbagai format tanggal diberikan untuk menyesuaikan pola aktif ParseRegex umum. Pola aktif Bilangan bulat digunakan untuk mengonversi string yang cocok menjadi bilangan bulat yang dapat diteruskan ke konstruktor DateTime.

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())

Output kode sebelumnya adalah sebagai berikut:

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

Pola aktif tidak hanya dibatasi untuk ekspresi pencocokan pola, Anda juga dapat menggunakannya pada pengikatan let.

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")

Output kode sebelumnya adalah sebagai berikut:

Hello, random citizen!
Hello, George!

Perhatikan bahwa hanya pola aktif kasus tunggal yang dapat diparameterkan.

// 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

Jenis Pengembalian untuk Pola Aktif Parsial

Pola aktif parsial mengembalikan Some () untuk menunjukkan kecocokan dan None sebaliknya.

Pertimbangkan kecocokan ini:

match key with
| CaseInsensitive "foo" -> ...
| CaseInsensitive "bar" -> ...

Pola aktif parsial untuk itu adalah:

let (|CaseInsensitive|_|) (pattern: string) (value: string) =
    if String.Equals(value, pattern, StringComparison.OrdinalIgnoreCase) then
        Some ()
    else
        None

Dimulai dengan F# 9, pola tersebut juga dapat mengembalikan bool:

let (|CaseInsensitive|_|) (pattern: string) (value: string) =
    String.Equals(value, pattern, StringComparison.OrdinalIgnoreCase)

Struktur Representasi untuk Pola Aktif Parsial

Secara default, jika pola aktif parsial mengembalikan option, maka akan ada alokasi untuk nilai Some ketika terjadi kecocokan yang berhasil. Untuk menghindarinya, Anda dapat menggunakan opsi nilai sebagai nilai pengembalian melalui penggunaan atribut Struct:

open System

[<return: Struct>]
let (|Int|_|) str =
   match Int32.TryParse(str) with
   | (true, n) -> ValueSome n
   | _ -> ValueNone

Atribut harus ditentukan, karena penggunaan kembalian struct tidak dapat disimpulkan hanya dengan mengubah jenis pengembalian menjadi ValueOption. Untuk informasi selengkapnya, lihat RFC FS-1039.

Pola aktif nol

Pada F# 9, pola aktif yang terkait dengan nullability ditambahkan.

Yang pertama adalah | Null | NonNull x |, yang merupakan cara yang direkomendasikan untuk menangani kemungkinan null. Dalam contoh berikut, parameter s disimpulkan nullable melalui penggunaan pola aktif ini:

 let len s =
    match s with
    | Null -> -1
    | NonNull s -> String.length s

Jika Anda lebih suka secara otomatis melemparkan NullReferenceException, Anda dapat menggunakan pola | NonNullQuick |:

let len (NonNullQuick str) =  // throws if the argument is null
    String.length str

Lihat juga