Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.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
F# 如何匹配一个值的多个副本?_F#_Pattern Matching - Fatal编程技术网

F# 如何匹配一个值的多个副本?

F# 如何匹配一个值的多个副本?,f#,pattern-matching,F#,Pattern Matching,F#的模式匹配功能非常强大,因此编写以下代码感觉很自然: match (tuple1, tuple2) with | ((a, a), (a, a)) -> "all values are the same" | ((a, b), (a, b)) -> "tuples are the same" | ((a, b), (a, c)) -> "first values are the same" // etc 但是,第一个模式匹配会产生编译器错误: 'a' is bound t

F#的模式匹配功能非常强大,因此编写以下代码感觉很自然:

match (tuple1, tuple2) with
| ((a, a), (a, a)) -> "all values are the same"
| ((a, b), (a, b)) -> "tuples are the same"
| ((a, b), (a, c)) -> "first values are the same"
// etc
但是,第一个模式匹配会产生编译器错误:

'a' is bound twice in this pattern
有没有比下面更干净的方法

match (tuple1, tuple2) with
| ((a, b), (c, d)) when a = b && b = c && c = d -> "all values are the same"
| ((a, b), (c, d)) when a = c && b = d -> "tuples are the same"
| ((a, b), (c, d)) when a = c -> "first values are the same"
// etc

这是F#的“活动模式”的完美用例。您可以这样定义其中的两个:

let (|Same|_|) (a, b) =
    if a = b then Some a else None

let (|FstEqual|_|) ((a, _), (c, _)) =
    if a = c then Some a else None
然后清理与它们匹配的模式;注意第一种情况(所有值都相等)如何使用嵌套的
相同的
模式来检查元组的第一个和第二个元素是否相等:

match tuple1, tuple2 with
| Same (Same x) ->
    "all values are the same"
| Same (x, y) ->
    "tuples are the same"
| FstEqual a ->
    "first values are the same"
| _ ->
    failwith "TODO"
性能提示:我喜欢用
inline
标记这些简单的活动模式——因为活动模式中的逻辑很简单(只有几个IL指令),所以内联它们并避免函数调用的开销是有意义的。

您可以用它来解决这个问题

let (|TuplePairPattern|_|) ((p1, p2), (p3, p4)) ((a, b), (c, d)) =
    let matched =
        [(p1, a); (p2, b); (p3, c); (p4, d)]
        |> Seq.groupBy fst
        |> Seq.map (snd >> Set.ofSeq)
        |> Seq.forall (fun s -> Set.count s = 1)
    if matched then Some () else None
特别是,您应该以文字(字符、字符串等)的形式定义模式


实际上,我可能会先解压缩元组,然后执行一系列if/then/else表达式:

let a,b = tuple1
let c,d = tuple2

if a = b && b = c && c = d then "all values are the same"
elif a = c && b = d then "tuples are the same"
elif a = c then "first values are the same"
...

如果您发现自己经常这样做,那么可能需要一个活动模式(在2元组的情况下,a是可行的,并且可能更可取-穷举匹配比非穷举匹配“更安全”)。或者,您可能需要更复杂的数据结构。

我认为,最优雅的方式可以通过结合@Stephen Swensen和@pad提供的两个优秀答案来实现

第一个想法是结构(一个包含两个元组的元组)可以解包一次,而不是在每个
match
情况下解包。
第二个想法是处理价值序列,所有这些价值序列必须彼此相等

代码如下:

let comparer ((a,b),(c,d)) =
    let same = Set.ofSeq >> Set.count >> ((=) 1)
    if   same[a; b; c; d]         then "all values are the same"
    elif same[a; c] && same[b; d] then "tuples are the same"
    elif same[a; c]               then "first values are the same"
    else                                "none of above"

您可以将
elif
更改为
匹配
,但对我来说似乎不可行。

仅为了消除一些等式检查,就需要引入大量开销。如果仅执行此
匹配
一次或两次,可能不会引起注意,但如果在循环某些数据时使用此选项(例如),则肯定会影响性能。同意。当可读性是关键,性能不是关键问题时,这是一个很好的方法。对于任何感兴趣的人来说:“F#的模式匹配非常强大”。F#继承的模式匹配的ML风格实际上故意没有那么强大。具体来说,ML将模式限制为所谓的线性模式,以确保匹配模式所需的时间量受模式大小的限制。在f*中,活动模式绕过这个,让你在模式匹配的中间做任何事情。缺点是性能不太可预测。Mathematica将这一点发挥到了极致。被否决的是(至少在我看来),抛弃了F#的模式匹配的优雅,转而支持非直接风格的链式if,else if's,完全忽略了函数式编程的意义。@DavidArno感谢您的反馈,但是,
if-then-else
概念甚至存在于范畴论和lambda演算中。你能解释一下为什么它会损害功能范式吗?而且,
匹配
不过是一个有序的
if-then
序列。被降级为(至少在IMO),抛弃了F#模式匹配的优雅,转而支持链接if,else-if的基本风格,完全忽略了函数式编程的要点。@DavidArno注意到,在F#
if-then-else
中,构造是函数表达式,而不是命令式语言中的语句。
let comparer ((a,b),(c,d)) =
    let same = Set.ofSeq >> Set.count >> ((=) 1)
    if   same[a; b; c; d]         then "all values are the same"
    elif same[a; c] && same[b; d] then "tuples are the same"
    elif same[a; c]               then "first values are the same"
    else                                "none of above"