활성 패턴

활성 패턴을 사용하면 입력 데이터를 세분화하는 명명된 파티션을 정의할 수 있으므로 구분된 공용 구조체와 마찬가지로 패턴 일치 식에서 이러한 이름을 사용할 수 있습니다. 활성 패턴을 사용하여 각 파티션에 대한 사용자 지정 방식으로 데이터를 분해할 수 있습니다.

구문

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

설명

이전 구문에서 식별자는 인수표현되는 입력 데이터의 파티션에 대한 이름이거나, 즉 인수의 모든 값 집합 하위 집합에 대한 이름입니다. 활성 패턴 정의에는 최대 7개의 파티션이 있을 수 있습니다. 식은 데이터를 분해할 폼을 설명합니다. 활성 패턴 정의를 사용하여 인수로 지정된 값이 속한 명명된 파티션을 결정하는 규칙을 정의할 수 있습니다. (| 및 |) 기호를 바나나 클립이라고 하며 이 유형의 let 바인딩에서 만든 함수를 활성 인식기라고 합니다.

예를 들어 다음과 같은 활성 패턴에 인수를 사용하는 것이 좋습니다.

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

다음 예제와 같이 패턴 일치 식에서 활성 패턴을 사용할 수 있습니다.

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

TestNumber 7
TestNumber 11
TestNumber 32

이 프로그램의 출력은 다음과 같습니다.

7 is odd
11 is odd
32 is even

활성 패턴의 또 다른 사용은 동일한 기본 데이터에 다양한 가능한 표현이 있는 경우와 같은 여러 가지 방법으로 데이터 형식을 분해하는 것입니다. 예를 들어 개체를 Color RGB 표현 또는 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"

위의 프로그램의 출력은 다음과 같습니다.

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

이러한 두 가지 방법으로 활성 패턴을 사용하면 데이터를 적절한 형식으로 분할 및 분해하고 계산에 가장 편리한 형식의 적절한 데이터에 대해 적절한 계산을 수행할 수 있습니다.

결과 패턴 일치 식을 사용하면 매우 읽기 쉬운 편리한 방식으로 데이터를 작성할 수 있으므로 잠재적으로 복잡한 분기 및 데이터 분석 코드가 크게 간소화됩니다.

부분 활성 패턴

경우에 따라 입력 공간의 일부만 분할해야 합니다. 이 경우 각각 일부 입력과 일치하지만 다른 입력과 일치하지 않는 부분 패턴 집합을 작성합니다. 항상 값을 생성하지 않는 활성 패턴은 부분 활성 패턴이라고 하며, 옵션 형식인 반환 값이 있습니다. 부분 활성 패턴을 정의하려면 바나나 클립 내의 패턴 목록 끝에 와일드 카드 문자(_)를 사용합니다. 다음 코드에서는 부분 활성 패턴의 사용을 보여 줍니다.

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"

이전 예제의 출력은 다음과 같습니다.

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

부분 활성 패턴을 사용하는 경우 개별 선택이 서로 분리되거나 상호 배타적일 수 있지만, 그렇지 않아도 됩니다. 다음 예제에서는 일부 숫자가 제곱과 큐브(예: 64)이므로 패턴 Square 및 패턴 큐브는 분리되지 않습니다. 다음 프로그램은 AND 패턴을 사용하여 정사각형 및 큐브 패턴을 결합합니다. 정수와 큐브뿐 아니라 정수와 큐브 모두인 정수는 최대 1,000개까지 출력됩니다.

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)

출력은 다음과 같습니다.

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

매개 변수가 있는 활성 패턴

활성 패턴은 항상 일치하는 항목에 대해 하나 이상의 인수를 사용하지만 추가 인수도 사용할 수 있습니다. 이 경우 매개 변수가 있는 이름 활성 패턴 이 적용됩니다. 추가 인수를 사용하면 일반 패턴을 특수화할 수 있습니다. 예를 들어 정규식을 사용하여 문자열을 구문 분석하는 활성 패턴은 종종 이전 코드 예제에 정의된 부분 활성 패턴을 Integer 사용하는 다음 코드와 같이 정규식을 추가 매개 변수로 포함합니다. 이 예제에서는 다양한 날짜 형식에 정규식을 사용하는 문자열을 지정하여 일반 ParseRegex 활성 패턴을 사용자 지정합니다. 정수 활성 패턴은 일치하는 문자열을 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())

이전 코드의 출력은 다음과 같습니다.

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

활성 패턴은 패턴 일치 식으로만 제한되지 않으며 let-bindings에서도 사용할 수 있습니다.

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

이전 코드의 출력은 다음과 같습니다.

Hello, random citizen!
Hello, George!

그러나 단일 사례 활성 패턴만 매개 변수화할 수 있습니다.

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

부분 활성 패턴에 대한 구조체 표현

기본적으로 부분 활성 패턴은 성공적인 일치 항목의 값에 대한 Some 할당을 포함하는 값을 반환 option 합니다. 또는 특성을 사용하여 값 옵션을 반환 값으로 사용할 Struct 수 있습니다.

open System

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

구조체 반환의 사용이 단순히 반환 형식 ValueOption을 변경에서 유추되지 않으므로 특성을 지정해야 합니다. 자세한 내용은 RFC FS-1039를 참조 하세요.

참고 항목