2017-09-10 4 views
4

종종 이질적인 객체 (다른 유형)의 모음을 반복 (지도, 반복 또는 접기)하려고합니다. 이를 처리하는 한 가지 방법은 차별화 된 노동 조합을 만드는 것인데,이를 통해 합법적으로 DU 케이스로 변환 된 오브젝트로 목록을 작성할 수 있습니다.차별 노동 조합의 구성원 목록을 통해이 반복을 단순화 할 수없는 이유는 무엇입니까?

type MYDU = | X1 of int 
      | X2 of float 
      | X3 of string 

let bar (y: MYDU) = 
    match y with 
    | X1 x -> printfn "%A" x 
    | X2 x -> printfn "%A" x 
    | X3 x -> printfn "%A" x 

[X1(1); X2(2.0); X3("3"); X1(4)] 
|> List.map bar |> ignore 

이 코드는 잘 실행 및 인쇄

1 
2.0 
"3" 
4 

대 : 다음 코드는 간단한 예제에서한다! 그러나 printfn으로 전화를 반복해서 피할 수 있는지 궁금합니다. 나는 다음을 시도하고 컴파일되지 않습니다 :

let baz (y: MYDU) = 
    match y with 
    | X1 x | X2 x | X3 x -> printfn "%A" x // red squiggly line under X1 x 

컴파일러 문제 메시지 :

This expression was expected to have type 'int' but here has type 'float'

나는 반복이 가능하다 피하는이 의심하지만 기본적인 실수를해야합니다. 어떤 제안?

+0

예제 스 니펫에서 판단하기가 어렵지만 실제로 이질적인 목록이있는 시나리오는 이러한 종류의 일이 귀찮은 것이 아닐 정도로 실제로는 희귀해야합니다. 코드 검토에서 가치가있을 수도 있습니다. 너무 낮은 수준에서 도메인을 모델링 한 것일 수 있습니다. – scrwtp

+0

일반적으로'List.map f |> ignore'를 할 때 언제나'List.iter'를 대신 사용해야합니다. 특히'f'가'unit'을 반환하는 함수 인 경우 (' bar '기능을 수행합니다). 'Seq.map'이 ** lazy **이기 때문에'List.map' 대신'Seq.map'을 사용하기로 바꿀 계획이라면 이것은 특히 중요합니다. 'Seq.iter'는 당신이 의도 한대로 seq 전체에서 반복됩니다. 'List.map' 대신에'List.iter'를 사용하면'Seq'를 사용하여 /로 전환 할 때 놀라지 않을 수 있습니다. – rmunn

답변

3

실수를 저 지르지 않고 있습니다. F #의 유형 시스템에서 허용 할 수있는 것이 아닙니다.

대/소문자 화살표 왼쪽에 여러 패턴이있을 수 있지만 동일한 값 집합 (유형 포함)을 바인딩해야합니다. 여기에서 x은 각 패턴마다 다른 유형을 가지고 있으며 컴파일러가 불평하기에 충분합니다.

고통을 덜어 줄 수있는 방법이 있습니다 (DU에 박스형 값을 반환하는 멤버가있을 수도 있고, 대/소문자에서 복싱을 수행 할 수있는 활성 패턴을 가질 수도 있지만). 상황. 패턴을 별도의 케이스로 분할하고 각각의 경우에 대해 오른쪽을 반복하는 것은 항상 진공 상태에서 더 나은 솔루션입니다.

+0

의'kvb '에 의한 (받아 들여진) 대답은 내가하고 싶은 것을 정확히 한 것처럼 보입니다. . 질문의 제목은 "F # Union 형식 목록에서 작동"입니다. 그래서 저는 아직도 혼란 스럽습니다. – Soldalma

+1

@Soldalma : 그 대답에서, 그가 묶는 값 ('''''''')은 대문자 경우 ('string'과'Section list')의 두 패턴 모두에서 같은 타입을가집니다. 여기서는 그렇지 않습니다.'x'는 각 패턴마다 다른 타입을가집니다. 그것만으로도 매치의 오른쪽이 개별적으로 그 값들과 함께 작동하더라도 컴파일을 실패하기에 충분합니다. – scrwtp

+0

알았어, 알았다. 감사. – Soldalma

2

대신 할 수있는 일은 인자를 일반적인 유형으로 변환 한 다음 그 값을 대신 인쇄하는 것입니다. 그리고 당신은 아직 패턴 매칭의 장점과 차별 노동 조합 : 여기

이 방법의 예를 얻을

type MYDU = 
    | X1 of int 
    | X2 of float 
    | X3 of string 

let bar y = 
    let myStr = 
     match y with 
     | X1 x -> string x 
     | X2 x -> string x 
     | X3 x -> x 
    printfn "%s" myStr 

bar (X1 5) 
+0

일반적인 경우에는 일반적인 유형으로 변환 할 수 없다고 생각합니다. – Soldalma

2

에 "복싱"값으로 어느 정도이 반복을 방지 할 수있다 obj 유형. 이것은 .NET의 System.Object : ".NET Framework의 모든 클래스의 궁극적 인 기본 클래스"의 별칭입니다. 즉, 모든 유형의 값도 obj입니다.

그러나 개체를 상자로 만들면 정적 입력을 잃게됩니다. F # 유형 시스템을 전복하고 오류 가능성을 높입니다. 이것이 그렇게하는 이유가 없다면 일반적으로 피해야 만하는 이유입니다.

함수 printfn "%A"은 모든 유형을 취할 수 있으므로 유형 서명이 효과적으로 obj -> unit입니다. 당신이 원하는 모든 값에이 함수를 실행하는 경우 복싱을 사용하는 것이 합리적이라고 간주 될 수 있습니다.당신은 가능하면이 유형의 안전을 잃고으로이 일을 피해야한다, 다시

let printMyDu myDu = 
    match myDu with 
    | X1 (Box x) 
    | X2 (Box x) 
    | X3 (Box x) -> printfn "%A" x 

이 같은 패턴을 사용하여 다음

let (|Box|) x = box x 

: 그리고 당신은 box 함수를 사용하여이 활성 패턴을 정의 할 수 있습니다 많은 경우에. 예를 들어 값을 입력하여 나중에 값의 유형을 확인하기 만하면 F #에 대해 잘못된 접근 방식을 사용하고있는 것입니다. 이 예제에서는 값 x을 상자에 넣은 다음 즉시 사용하고 버려 전체 유형 안전을 줄이지 않습니다.