Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
混合DU和x27时的F#模式匹配;s和其他值_F#_Pattern Matching - Fatal编程技术网

混合DU和x27时的F#模式匹配;s和其他值

混合DU和x27时的F#模式匹配;s和其他值,f#,pattern-matching,F#,Pattern Matching,表达以下代码的最有效方式是什么 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

表达以下代码的最有效方式是什么

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返回一个布尔结果,其中false应返回null 如果为true,则应再次运行整个块(将其包装在名为next的func中) 或者,如果找到了break的特殊值,则循环应该退出并返回break值


有没有办法将这段代码压缩成更小的内容?

我认为您编写的代码很好。这里有一个我稍微喜欢的替代方案:

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

我不太喜欢匹配布尔值而不是使用
if-else
。那怎么办

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
或者,如果您认为不需要特殊的
isBreak
函数(我理解这一点),那么让我们尝试创建一个更通用的函数:C#s
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
让我们尝试播放一些x
|无
...
如果是cond.EvalBool(),则
将body.Eval()|>tryCast与
|Some(Break u作为e)->box e//Break是ControlFlowModifier的DU元素
|->next()//所有其他值都应调用next()
否则无效

我最终为此创建了一个活动模式。 其他地方也存在类似的逻辑,因此我可以使其可重用

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

看起来不错?

我想指出的是,
Eval
的结果类型似乎有一个子类型层次结构,如果它也是一个DU,那么您可以执行以下操作

match body.Eval() with
| ControlFlowModifier(Break e) -> box e
| _ -> next()
嵌套模式万岁。

如果你的意思是“最有效的方式”作为最短的代码,我也投票给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

要展平嵌套的
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  
编辑:正如罗杰在评论中指出的那样,这个版本的代码可能不太可读。我认为更好的选择是只使用
TryCast
,并稍微改变原始代码的格式(虽然这不是完全标准的缩进,但它是正确的,而且F#compiler处理得很好):

这可能是基于模式匹配的最具可读性的选项,但您也可以使用kvb版本中第一个
匹配的
instad,并将其与
TryCast
结合使用(这实际上取决于个人偏好):


在任何情况下,我相信
TryCast
会使代码更具可读性,因为您避免了一个嵌套(因为
:?…as.
)而需要另一个嵌套)。

@Kha-奇怪的是,我们的方法是多么相似。事实上,我也打算使用
box e
而不是
e:>obj
,但我认为这完全是一种个人风格的东西……是的,嵌套模式非常棒。当你开始引入活动模式时,情况就更糟了。嗯,把DU类型的名称放在前面,然后像这样把DU元素放在里面似乎不起作用?无法将其编译此假设body.Eval()返回类型为Foo的值,这是一个具有大小写“ControlFlowModifier of Bar”的DU,其中Bar也是一个具有大小写“Break of Scope”或其他任何内容的DU。虽然我确实觉得这种方法在技术上很有趣,但代码的可读性几乎消失了(IMO)。@罗杰:是的,我觉得原来的代码已经可以了。也许只使用<>代码> Tycase将是最好的选择。对你的问题不是真正的答案,但是,如果你的代码看起来像这样(使用<代码>:/<代码>和<代码> null < /代码>),那么你可以考虑一个更惯用的解决方案或包装器。
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 
match cond.EvalBool() with 
| false -> null 
| true ->                 
match body.Eval() with 
| TryCast(Break(scope) as e) -> e :> obj
| _ -> next()
if cond.EvalBool() then
  match body.Eval() with 
  | TryCast(Break(scope) as e) -> e :> obj
  | _ -> next()
else null