2010-04-23 4 views
2

다음 코드를 표현하는 가장 효과적인 방법은 무엇입니까?DU 및 다른 값을 혼합 할 때 F # 패턴 일치

match cond.EvalBool() with 
| true ->     
    match body.Eval() with 
    | :? ControlFlowModifier as e -> 
     match e with 
     | Break(scope) -> e :> obj //Break is a DU element of ControlFlowModifier 
     | _ -> next() //other members of CFM should call next() 
    | _ -> next() //all other values should call next() 
| false -> null 

cond.EvalBool은 반환해야합니다 거짓 부울 결과를 반환 널 다시 전체 블록을 실행해야 하나 사실 (그 다음이라는 FUNC에 싸여) 또는 휴식의 특수 값이 발견되는 경우, 루프는 종료하고 중단 값을 리턴해야합니다.

코드 블록을 압축하여 압축 할 수 있습니까?

+0

질문에 대한 답변은 아니지만 코드가 (:?과'null '을 사용하여) 보이는 경우보다 관용적 인 F # 솔루션이나 래퍼를 고려해보십시오. –

답변

4

내가 작성한 코드는 훌륭하다고 생각합니다.

let isBreak = function | Break(_) -> true | _ -> false 
if cond.EvalBool() then 
    match body.Eval() with 
    | :? ControlFlowModifier as e when isBreak e -> e :> obj 
    | _ -> next() 
else 
    null 
+0

글쎄, 당신이 날 때려 : –

+0

@Kha - 그것은 우리의 접근 방식이 얼마나 비슷한지 불안합니다. 사실, 나는 또한'e :> obj' 대신에'box e'를 사용할 것이지만, 그것은 엄격하게 개인적인 것으로 결정했습니다. – kvb

2

나는 논리 값을 일치하는 대신 if-else를 사용하여 너무 좋아 아니에요 : 여기 소폭 선호하는 대안이다. 특수 isBreak 기능이 필요하지 않을 것을 생각하면,보다 일반적인 함수를 만드는 시도 할 수 있습니다 무엇을 (내가 이해 것)

let isBreak = function Break _ -> true | _ -> false 
... 

if cond.EvalBool() then 
    match body.Eval() with 
    | :? ControlFlowModifier as e when isBreak e -> box e 
    | _ -> next() 
else null 

또는

에 대해 : C 번호의 as 운영자

let tryCast<'T> (o : obj) = 
    match o with 
    | :? 'T as x -> Some x 
    | _ -> None 
... 

if cond.EvalBool() then 
    match body.Eval() |> tryCast with 
    | Some (Break _ as e) -> box e //Break is a DU element of ControlFlowModifier 
    | _ -> next() //all other values should call next() 
else null 
1

I 결국이 패턴이 활성화되었습니다. 비슷한 논리는 그래서 그것을 재사용

let rec next() : obj = 
if cond.EvalBool() then 
    match body.Eval() with 
    | IsBreak(res) -> res 
    | _ -> step.Eval() |> ignore ; next() 
else null 

괜찮은 보이는 만들 수있는 다른 존재 하는가?

3

나는 거기 Eval의 결과 유형에 대한 하위 계층 구조, 그리고 대신도 DU했다가 있다면, 당신은 중첩 된 패턴

match body.Eval() with 
| ControlFlowModifier(Break e) -> box e 
| _ -> next() 

만세처럼 뭔가를 할 수 나타납니다 것을 지적하고 싶다.

+0

네, 중첩 된 패턴은 굉장합니다. 활동 패턴을 도입하기 시작하면 더욱 그렇습니다. – gradbot

+0

음, DU 유형 이름을 먼저 넣고 DU 요소를 그 안에 넣는 것처럼 보이지 않습니까? –

+0

이 코드는 body.Eval()이 "ControlFlowModifier of Bar"라는 대/소문자를 가진 DU 인 Foo 유형의 값을 반환하며 Bar는 "Break of Scope" 또는 무엇이든. – Brian

0

이 경우 당신이 짧은 코드로 "가장 효과적인 방법"을 의미, 내가 너무 AP에 투표 :

let (|CondEval|_|) (c,_) = if c.EvalBool() then Some true else None 
let (|BodyEval|_|) (_,b) = 
    match b.Eval() with 
    | ControlFlowModifier as e -> Some e 
    | _ -> None 

match cond,body with 
| CondEval _ & BodyEval e -> e :> obj 
| true -> next() 
| false -> null 
1

중첩 match 구조를 평평하게하려면 중첩 된 패턴을 사용해야합니다. 이것은 차별화 된 노동 조합 (Brian이 지적한대로)에서 가장 잘 작동합니다. 차별화 된 노동 조합을 사용하도록 F # 코드를 설계하는 것이 최선의 방법입니다.

그렇지 않으면 match을 사용하여 코드를 간결하게 작성하려는 경우 (활성화 된 패턴을 보여주는 한 가지 예제를 게시 한 ssp).

let (|TryCast|_|) a : 'res option =  
    match (box a) with 
    | :? 'res as r -> Some(r) 
    | _ -> None 

let (|Value|) (l:Lazy<_>) = l.Value 

첫 번째는 :?처럼이지만, (as 불가능) 값과 일치하는 둥지 다른 패턴을 수행 할 수 있습니다 : 그러나, 당신은 다음과 같은 두 가지 재사용 가능한 활성 패턴을 사용하여이 작업을 수행 할 수 있습니다. 두 번째 것은 lazy value의 평가를 강요합니다 (둘 다 F # 라이브러리에서 선언 할 수 있다고 가정합니다).지금 당신은 쓸 수 있습니다 :

match lazy cond.EvalBool(), lazy body.Eval() with 
| Value(true), Value(TryCast((Break(scope) : ControlFlowModifier)) as e) -> 
    e :> obj //Break is a DU element of ControlFlowModifier 
| Value(true), _ -> 
    next() //all other values should call next() 
| _, _ -> null 

편집 : 로저 댓글에서 지적은, 코드의이 버전은 매우 읽을 수 없습니다. 나는 더 나은 옵션은 TryCast를 사용하고 약간 다르게 원래의 코드를 포맷하는 것입니다 생각 (이 완전히 표준 들여 쓰기 아니지만, 그것은 정확하고 F # 컴파일러 잘 그것을 처리) :

match cond.EvalBool() with 
| false -> null 
| true ->     
match body.Eval() with 
| TryCast(Break(scope) as e) -> e :> obj 
| _ -> next() 

이 아마입니다 대부분의 읽을 수있는 옵션 (이 정말 개인 환경 설정에 따라 다름) 패턴 매칭 기반으로하지만, 당신은 또한 KVB하여 버전으로 처음 matchif instad를 사용하고 TryCast와 결합 수 :

어떤 경우
if cond.EvalBool() then 
    match body.Eval() with 
    | TryCast(Break(scope) as e) -> e :> obj 
    | _ -> next() 
else null 

, TryCast은 하나의 중첩을 피할 때 더 쉽게 읽을 수있는 코드 (:? .. as ..로 인해 다른 것이 필요함).

+0

이 접근법이 기술적으로 흥미로운 동안 코드의 판독 가능성은 거의 사라졌습니다 (IMO). –

+0

@ 로저 : 예, 원래 코드가 이미 괜찮다고 생각합니다. 아마도'TryCast' 만 사용하는 것이 최선의 선택 일 것입니다. –