다음을 통해 공유


활성 패턴(F#)

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

// Complete active pattern definition.
let (|identifer1|identifier2|...|) [ arguments ] = expression
// Partial active pattern definition.
let (|identifier1|identifier2|...|_|) [ arguments ] = expression

설명

위 구문에서 식별자는 arguments로 표현되는 입력 데이터의 파티션에 대한 이름, 즉 인수의 모든 값으로 이루어진 집합의 하위 집합에 사용되는 이름입니다. 활성 패턴 정의에 최대 7 개의 파티션이 있을 수 있습니다. expression은 데이터를 어떤 형식으로 분해할지 설명합니다. 인수로 주어진 값이 속하는 명명된 파티션의 결정과 관련하여 이 결정 규칙을 정의하는 데 활성 패턴 정의를 사용할 수 있습니다. (| 및 |) 기호를 바나나 클립이라고 하며 이 형식의 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
 R: 255 G: 0 B: 0
 H: 0.000000 S: 1.000000 B: 0.500000
Black
 R: 0 G: 0 B: 0
 H: 0.000000 S: 0.000000 B: 0.000000
White
 R: 255 G: 255 B: 255
 H: 0.000000 S: 0.000000 B: 1.000000
Gray
 R: 128 G: 128 B: 128
 H: 0.000000 S: 0.000000 B: 0.501961
BlanchedAlmond
 R: 255 G: 235 B: 205
 H: 36.000000 S: 1.000000 B: 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.

부분 활성 패턴을 사용할 때는 개별 선택이 서로 연결되지 않거나 상호 배타적일 수도 있지만 반드시 그런 것은 아닙니다. 다음 예제에서 패턴 Square와 패턴 Cube 사이에는 연결 지점이 있습니다. 64 같이 사각형인 동시에 입방체인 몇몇 숫자가 있기 때문입니다. 다음 프로그램은 사각형인 동시에 입방체인 1000000 이하의 모든 정수를 출력합니다.

let err = 1.e-10

let floatequal x y =
   abs (x - y) < err

let (|Square|_|) (x : int) =
  if floatequal (sqrt (float x)) (round (sqrt (float x))) then Some(x)
  else None

let (|Cube|_|) (x : int) =
  if floatequal ((float x) ** ( 1.0 / 3.0)) (round ((float x) ** (1.0 / 3.0))) then Some(x)
  else None

let examineNumber x =
   match x with
      | Cube x -> printfn "%d is a cube" x
      | _ -> ()
   match x with
      | Square x -> printfn "%d is a square" x
      | _ -> ()

let findSquareCubes x =
   if (match x with
       | Cube x -> true
       | _ -> false
       &&
       match x with
       | Square x -> true
       | _ -> false
      )
   then printf "%d \n" x

[ 1 .. 1000000 ] |> List.iter (fun elem -> findSquareCubes elem)

출력은 다음과 같습니다.

1
64
729
4096
15625
46656
117649
262144
531441
1000000

매개 변수가 있는 활성 패턴

활성 패턴은 일치 여부를 확인할 항목에 대해 항상 적어도 한 개 이상의 인수를 갖지만 인수를 추가로 취할 수도 있습니다. 이와 같은 패턴을 매개 변수가 있는 활성 패턴이라고 합니다. 추가 인수를 사용하면 일반 패턴을 특수화할 수 있습니다. 예를 들어 정규식을 사용하여 문자열을 구문 분석하는 활성 패턴에는 다음 코드에서와 같이 정규식이 추가 매개 변수로 포함되는 경우가 많습니다. 다음 코드에서는 이전 코드 예제를 통해 정의한 Integer 부분 활성 패턴도 사용합니다. 이 예제에서는 일반 ParseRegex 활성 패턴을 사용자 지정하기 위해 다양한 날짜 형식에 대한 정규식을 제공하는 문자열이 주어집니다. 일치하는 문자열을 DateTime 생성자에 전달할 수 있는 정수로 변환하는 데는 Integer 활성 패턴이 사용됩니다.

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

참고 항목

참조

일치 식(F#)

기타 리소스

F# 언어 참조