>

구분 된 유니온 태그의 유형을 다른 함수에 전달하여 패턴 일치에 사용할 수 있습니까?

무의미한 실례 :

type Animal = Pig of string | Cow of string | Fish of string
let animals = [Pig "Mike"; Pig "Sarah"; Fish "Eve"; Cow "Laura"; Pig "John"]
let rec filterAnimals animalType animals =
    if animals = [] then
        []
    else
        let rest = filterAnimals animalType (List.tail animals)
        match List.head animals with
        |animalType animal -> animal::rest // <- this doesn't work
        |_ -> rest
printfn "%A" (filterAnimals Pig animals)


  • 답변 # 1

    차별화 된 노조는 사례간에 의미 겹침이없는 경우 가장 잘 작동합니다.

    예에서, 각 경우에는 동일한 의미의 string 를 가진 동일한 구성 요소가 있습니다.  "동물의 이름"을 나타냅니다. 그러나 그것은 의미 상 중복입니다! 차별적 인 노동 조합은 당신이 원하지 않는 것을 구별하도록 강요 할 것입니다 : 당신은 "돼지의 이름"과 "소의 이름"을 구별하도록 강요하고 싶어하지 않습니다;"동물의 이름"을 생각하고 싶을뿐입니다.

    보다 적합한 유형을 만들어 봅시다 :

    type Animal = Pig  | Cow  | Fish
    type Pet = Animal * string
    let animals = [(Pig, "Mike"); (Fish, "Eve"); (Pig, "Romeo")
    
    

    이 유형으로 비 Pig 필터링 s는 하나의 라이너입니다 :

    animals |> List.filter (fst >> (=) Pig)
    
    

    모든 동물에 이름이없는 경우 옵션 유형을 사용하십시오 :

    type Pet = Animal * string option
    
    
    예를 들어, 모든 Pig 를 알고 있다면

    당신은당신의 동물들에게 차별적 인 노조를 사용할 것입니다  이름은 있지만 Fish 는 없습니다  그 경우 : 그 경우에는 겹치지 않습니다.

  • 답변 # 2

    filterAnimals 를 변경할 수 있습니다  부분 활성 패턴을 입력으로 취하는 기능 :

    let rec filterAnimals (|IsAnimalType|_|) animals =
        if animals = [] then
            []
        else
            let rest = filterAnimals (|IsAnimalType|_|) (List.tail animals)
            match List.head animals with
            | IsAnimalType animal -> animal::rest
            | _ -> rest
    
    

    그런 다음 돼지에 대한 능동 부분 패턴을 정의 할 수 있습니다 :

    let (|IsPig|_|) candidate =
        match candidate with
        | Pig(_) -> Some candidate
        | _ -> None
    
    

    그리고 다음과 같이 함수를 호출 할 수 있습니다 (FSI 예) :

    > filterAnimals (|IsPig|_|) animals;;
    val it : Animal list = [Pig "Mike"; Pig "Sarah"; Pig "John"]
    
    
    <시간>

    실제로 다음과 같이 부분 활성 패턴을 줄일 수 있습니다 :

    let (|IsPig|_|) = function | Pig(x) -> Some(Pig(x)) | _ -> None
    
    

    그리고 당신은 그것들을 인라인 할 수 있다는 것이 밝혀졌습니다 :

    > filterAnimals (function | Pig(x) -> Some(Pig(x)) | _ -> None) animals;;
    val it : Animal list = [Pig "Mike"; Pig "Sarah"; Pig "John"]
    > filterAnimals (function | Fish(x) -> Some(Fish(x)) | _ -> None) animals;;
    val it : Animal list = [Fish "Eve"]
    > filterAnimals (function | Cow(x) -> Some(Cow(x)) | _ -> None) animals;;
    val it : Animal list = [Cow "Laura"]
    
    

  • 답변 # 3

    완벽 성을 위해이 솔루션을 나열하겠습니다.
    입력 한 내용을인용하면 태그 이름을 추론 할 수 있습니다.

    open Microsoft.FSharp.Quotations
    type Animal = Pig of string | Cow of string | Fish of string
    let isAnimal (animalType : Expr) (animal : Expr) =
        match animal with
            | NewUnionCase(at, _) ->
                match animalType with
                    | Lambda (_, NewUnionCase (aatt, _)) -> aatt.Name = at.Name
                    | _ -> false
            | _ -> false
    let animal = <@ Pig "Mike" @>
    isAnimal <@ Pig @> animal  // True
    isAnimal <@ Cow @> animal  // False
    
    

    이것은 꽤 장황한 것이지만, 단일 값 대신 목록을 인용하고 싶다면 더욱 더 커질 것입니다.

    동물 유형 만 인용하는 약간 다른 버전으로, 필요에 따라 동물 목록을 쉽게 필터링 할 수 있습니다 (문자열에 대한 의심스러운 비교 가격으로) :

    open Microsoft.FSharp.Quotations
    type Animal = Pig of string | Cow of string | Fish of string
    let isAnimal (animalType : Expr) animal =
        match animalType with
            | Lambda (_, NewUnionCase (aatt, _)) -> animal.ToString().EndsWith(aatt.Name)
            | _ -> false
    let animal = Pig "Mike"  // No quote now, so we can process Animal lists easily
    isAnimal <@ Pig @> animal  // True
    isAnimal <@ Cow @> animal  // False
    let animals = [Pig "Mike"; Pig "Sarah"; Fish "Eve"; Cow "Laura"; Pig "John"]
    let pigs = animals |> List.filter (isAnimal <@ Pig @>)
    
    

    후자의 버전은 태그 이름을 문자열로 전달하는 것보다 실제로 우수하지 않습니다.

  • 답변 # 4

    아니요. 태그와 태그를 별도로 처리 할 수 ​​있도록 태그 만 전달할 수는 없습니다. 문자열 다음과 같이 정의 할 수 있습니다 :

    type AnimalType = Pig  | Cow  | Fish
    type Animal = Animal of AnimalType * string
    let animals = [Animal (Pig, "Mike"); Animal (Pig, "Sarah"); Animal (Fish, "Eve"); Animal (Cow, "Laura"); Animal (Pig, "John")]
    let rec filterAnimals animalType animals =
        if animals = [] then
            []
        else
            let rest = filterAnimals animalType (List.tail animals)
            match List.head animals with
            | Animal (x, animal) when x = animalType -> animal::restwork
            |_ -> rest
    printfn "%A" (filterAnimals Pig animals)
    
    
    또는 pyzwyz의 튜플 만 사용할 수도 있습니다.

    업데이트

    구조가 항상 동일하지 않은 경우 어떻게되는지에 대한 의견에서 귀하의 질문에 관해서는 사용할 수있는 트릭이 있습니다. 각 태그가 다른 하위 클래스로 컴파일되기 때문에 두 가지 차별 조합의 유형을 비교할 수 있습니다.

    AnimalType * string
    
    

    이러한 방법으로 가기 전에 이런 식으로 문제를 모델링해야하는지 생각해야합니다.

    이 구조를 사용하기로 결정하더라도 내장 필터 기능을 사용하는 것이 좋습니다. @polkduran이 제안한 솔루션을 참조하십시오.

  • 답변 # 5

    이는 귀하의 질문에 직접 대답하지는 않지만 대안을 제안합니다. 당신이 원하는 것을 달성하는 방법. 기존의 고차 함수 type Animal = | Person of string * string | Pig of string | Cow of string | Fish of string let animals = [Pig "Mike"; Pig "Sarah"; Fish "Eve"; Cow "Laura"; Pig "John"] let rec filterAnimals animalType animals = if animals = [] then [] else let rest = filterAnimals animalType (List.tail animals) match List.head animals with | x when animalType.GetType() = x.GetType() -> x::rest |_ -> rest printfn "%A" (filterAnimals (Pig "") animals) 를 사용하여 목록을 필터링 할 수 있습니다.  패턴 매칭 :

    List.filter
    
    

    이것은 좀 더 관용적 인 방법이라고 생각합니다. 패턴을 사용하여 목록을 필터링합니다.이 경우 패턴 let pigs = animals |> List.filter (function |Pig(_)-> true |_->false ) 를 만족하는 사람 만 유지하면서 동물을 필터링합니다. .

    Pig(_)

  • 이전 xcode - 기본 phonegap/Cordova 내장 iOS 앱에서 키보드 모양 변경
  • 다음 jquery - Javascript를 통해 CSS를 통해 정확한 RGBa 값을 설정하는 방법은 무엇입니까?