F#短路模式匹配

6

如果你是F#的新手,下面的问题可能一点都不说得通。

// an attempt at Huffman encoder 
let encodeValue x y = function ...

match ((encodeValue left value), (encodeValue right value)) with
    | ((true, encoded), (_, _)) -> (true, encoded + "1")
    | ((_, _), (true, encoded)) -> (true, encoded + "0")
    | ((false, _), (false, _)) -> (false, "")
    | _ -> failwith "Error"

在真实环境中,encodeValue 可能非常昂贵。是否可能(或合理)要求 F# 首先评估 encodeValue left value ,尝试匹配,然后在必要时执行 encodeValue right value?
4个回答

4

你可以通过使用惰性值和活动模式来很好地模拟短路:

//not sure why function Lazy.force is recognized in VS but not in FSI
//need to use member Force()
let (|Force|) (l:Lazy<_>) =
    l.Force()

let encodeValue x y = function ...

match (encodeValue left value), lazy(encodeValue right value) with
    | (true, encoded), _                    -> (true, encoded + "1")
    | _              , Force(true, encoded) -> (true, encoded + "0")
    | (false, _)     , Force(false, _)      -> (false, "")
    | _                                     -> failwith "Error"
Lazy值计算0或1次:如果你从未在它们上面调用Force(),它们将永远不会被计算。第一次调用Force()时,它们被计算并保存结果,以供您每次调用Force()时使用。
这里的(|Force|)是一个完整的活动模式,一个非常好的功能,允许您实现自定义模式匹配结构。
请注意,正如@Brian指出的那样,在可能进行短路的lazy值位置需要使用_。如果(true, encoded)匹配,则不会强制执行惰性、昂贵的计算。然后,对于每个其他情况,使用(|Force|)活动模式进行多个匹配只会使用第一个事件的结果。 更新 @Daniel指出,F#已经有了一个活动模式,可以完全做到(|Force|)所做的事情:http://msdn.microsoft.com/en-us/library/ee340223.aspx

我认为你需要将第一行的 Force(_,_) 改为 _ - Brian
1
Lazy.force 在 Power Pack 中 (https://github.com/fsharp/powerpack/blob/master/src/FSharp.PowerPack.Compatibility/lazy.fs),所以你需要 #r "FSharp.PowerPack.Compatibility"。 - Robert
2
@Stephen:这种活动模式是内置的。链接 - Daniel
@Daniel:哈!从来没注意到那个 - Stephen Swensen

3
活动模式的解决方案更加酷炫,但我认为提到一个简单的嵌套匹配也值得一提:

  match encodeValue left value with
    | true, e -> true, e + "1"
    | false, _ -> 
      match encodeValue right value with
        | true, f -> true, f + "0"
        | _ -> false, ""

2

这里有另一种活动模式的解决方案。将部分应用的函数传递给活动模式:

let (|Encoded|_|) f x = 
  match f x with
  | true, encoded -> Some encoded
  | _ -> None

match value with
  | Encoded (encodeValue left) encoded -> (true, encoded + "1")
  | Encoded (encodeValue right) encoded -> (true, encoded + "0")
  | _ -> (false, "")

我删除了最后一个模式,因为它永远不会匹配。


1

由于F#不是惰性函数语言(尽管可以显式地使表达式求值变得惰性)。 你可以像下面展示的那样,分解模式匹配。

let a() = match encodeValue left value with
          | (true,encoded) -> (true,encoded + "1")
          | _ -> (false,"")
let b() = match encodeValue right value with
          | (true,encoded) -> (true,encoded + "0")
          | _ -> (false,"")
match a() with
| (false,_) -> b()
| r -> r

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接