구분 된 유니온 태그의 유형을 다른 함수에 전달하여 패턴 일치에 사용할 수 있습니까?
무의미한 실례 :
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
- 답변 # 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)
업데이트
구조가 항상 동일하지 않은 경우 어떻게되는지에 대한 의견에서 귀하의 질문에 관해서는 사용할 수있는 트릭이 있습니다. 각 태그가 다른 하위 클래스로 컴파일되기 때문에 두 가지 차별 조합의 유형을 비교할 수 있습니다.
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(_)
관련 자료
- 인수의 이름을 지정하여 Rust에서 함수를 호출 할 수 있습니까?
- Go에서 명명 된 인수를 가진 함수를 호출 할 수 있습니까?
- c++ - std - : bind가 바운드 함수에 가능한 것보다 많은 인수를 허용하는 이유는 무엇입니까?
- python - argparse를 사용하여 하이픈 (-)없이 가능한 인수 목록을 만드는 방법
- ember.js - Ember Power Select에서 onChange 액션에 여러 인수를 전달할 수 있습니까?
- graphql - 런타임에 리터럴 유형의 Typescript Union에서 값을 생성 할 수 있습니까?
- java - 인수와 본문을 사용할 때 개조가 잘못된 요청은 어떻게 가능합니까?
- 식별 키에서 TypeScript 식별 통합 필터링이 실패 함
- f# - 타입 주석과 패턴 매칭
- F # 일치하지 않는 차별 조합 내의 값 추출
- pattern matching - 목록의 각 요소 값을 목록 내의 다른 모든 요소와 비교하고 업데이트 (f #)
- F # 차별적 노동 조합 가치를"슈퍼 셋"노동 조합으로"업 캐스트"할 수 있습니까?
- F # 명령 줄 인수를 구문 분석합니다이 시점 또는 그 이전에 불완전한 구조화 된 구문이 예상됩니다 또는 ;;
- 카운트 다운 카운트 업 F # 재귀
- syntax - 기본 유형이 3 가지 인 차별 조합 유형 목록 작성
- f# - 중첩 된 일치 표현식을 함수로 변환
- F # 차별적 노조의 기본 유형 사용
- types - 패턴 일치 (F #)의 문자열 입력을 기반으로 다른 판별 조합 상태 리턴
차별화 된 노조는 사례간에 의미 겹침이없는 경우 가장 잘 작동합니다.
예에서, 각 경우에는 동일한 의미의
string
를 가진 동일한 구성 요소가 있습니다. "동물의 이름"을 나타냅니다. 그러나 그것은 의미 상 중복입니다! 차별적 인 노동 조합은 당신이 원하지 않는 것을 구별하도록 강요 할 것입니다 : 당신은 "돼지의 이름"과 "소의 이름"을 구별하도록 강요하고 싶어하지 않습니다;"동물의 이름"을 생각하고 싶을뿐입니다.보다 적합한 유형을 만들어 봅시다 :
이 유형으로 비
Pig
필터링 s는 하나의 라이너입니다 :모든 동물에 이름이없는 경우 옵션 유형을 사용하십시오 :
예를 들어, 모든Pig
를 알고 있다면당신은당신의 동물들에게 차별적 인 노조를 사용할 것입니다 이름은 있지만
Fish
는 없습니다 그 경우 : 그 경우에는 겹치지 않습니다.