Pola Aktif

Pola aktif memungkinkan Anda menentukan partisi bernama yang membagi lagi data input, sehingga Anda dapat menggunakan nama tersebut dalam ekspresi pencocokan pola seperti pada gabungan terdiskriminasi. Anda dapat menggunakan pola aktif untuk menguraikan data dengan cara yang disesuaikan untuk setiap partisi.

Sintaks

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

Keterangan

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 bentuk untuk data yang diuraikan. Anda dapat menggunakan definisi pola aktif guna menentukan aturan untuk menentukan partisi bernama mana yang memiliki nilai yang diberikan sebagai argumen. Simbol (| dan |) disebut sebagai banana clip 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 dari program ini adalah sebagai berikut:

7 is odd
11 is odd
32 is even

Penggunaan lain dari pola aktif adalah menguraikan jenis data dalam berbagai cara, seperti ketika data yang mendasar yang sama memiliki beragam 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 dari 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 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 sangat mudah dibaca, sangat menyederhanakan pencabangan dan kode analisis data yang berpotensi kompleks.

Pola Aktif Parsial

Terkadang, Anda hanya perlu mempartisi sebagian ruang input. Dalam hal ini, Anda menulis serangkaian pola parsial yang masing-masing cocok dengan beberapa input tetapi gagal cocok dengan 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, gunakan karakter wildcard (_) di akhir daftar pola di dalam banana clip. 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 putus-putus atau saling eksklusif, tetapi sebenarnya tidak perlu. Dalam contoh berikut, Square pola dan Cube pola tidak terputus-putus, karena beberapa angka adalah pangkat dua dan pangkat tiga, seperti 64. Program berikut menggunakan pola AND untuk menggabungkan pola Square dan Cube. Program ini mencetak semua bilangan bulat hingga 1000 yang merupakan pangkat dua dan pangkat tiga, serta pangkat tiga saja.

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 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 dicocokkan, tetapi pola aktif juga dapat mengambil argumen tambahan,di mana nama pola aktif berparameter diterapkan. 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 yang 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 mengubah 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 dari 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 dibatasi hanya untuk ekspresi pencocokan pola, Anda juga dapat menggunakannya pada let-binding.

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 dari kode sebelumnya adalah sebagai berikut:

Hello, random citizen!
Hello, George!

Namun, perhatikan bahwa hanya pola aktif satu huruf 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

Representasi Struct untuk Pola Aktif Parsial

Secara default, pola aktif parsial mengembalikan nilai option, yang akan melibatkan alokasi untuk nilai Some pada kecocokan yang berhasil. Atau, Anda dapat menggunakan opsi nilai sebagai nilai yang dikembalikan 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 pengembalian struct tidak disimpulkan hanya dengan mengubah jenis pengembalian menjadi ValueOption. Untuk informasi selengkapnya, lihat RFC FS-1039.

Lihat juga