Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/xpath/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
F# 模式匹配有多聪明?_F#_Pattern Matching - Fatal编程技术网

F# 模式匹配有多聪明?

F# 模式匹配有多聪明?,f#,pattern-matching,F#,Pattern Matching,我的程序大部分时间都花在数组模式匹配上,我想知道是否应该重写函数并放弃自动模式匹配 例如,一个非常简单的案例 let categorize array = match array with | [|(1|2);(1|2);(1|2)|] -> 3 | [|(1|2);(1|2);_|] -> 2 | [|(1|2);_;_|] -> 1 | _ -> 0 categorize [|2;1;3|] 编译器是否会在这种情况下应用最少

我的程序大部分时间都花在数组模式匹配上,我想知道是否应该重写函数并放弃自动模式匹配

例如,一个非常简单的案例

let categorize array =
    match array with
    | [|(1|2);(1|2);(1|2)|] -> 3
    | [|(1|2);(1|2);_|] -> 2
    | [|(1|2);_;_|] -> 1
    | _ -> 0

categorize [|2;1;3|]
编译器是否会在这种情况下应用最少数量的比较,例如,识别第一种情况与第二种情况相同,但第三种元素除外


实际上,模式更复杂,预优化的模式匹配可能比完全优化的模式匹配花费更多的时间。

直接从反射镜:

公共静态int分类(int[]数组)
{
如果((array>null)&&(array.Length==3))
{
开关(数组[0])
{
案例1:
交换机(阵列[1])
{
案例1:
交换机(阵列[2])
{
案例1:
案例2:
goto标签_005C;
}
goto标签_005A;
案例2:
交换机(阵列[2])
{
案例1:
案例2:
goto标签_005C;
}
goto标签_005A;
}
转到标签_0042;
案例2:
交换机(阵列[1])
{
案例1:
交换机(阵列[2])
{
案例1:
案例2:
goto标签_005C;
}
goto标签_005A;
案例2:
交换机(阵列[2])
{
案例1:
案例2:
goto标签_005C;
}
goto标签_005A;
}
转到标签_0042;
}
}
返回0;
标签_0042:
返回1;
标签_005A:
返回2;
标签_005C:
返回3;
}
我看不出任何低效的东西。

你可以写:

let f (xs: _ []) =
  if xs.Length=3 then
    let p n = n=1 || n=2
    if p xs.[0] then
      if p xs.[1] then
        if p xs.[2] then 3
      else 2
    else 1
  else 0

你的问题真正缺少的是实际的主题领域。换句话说,您的问题是非常通用的(这通常是很好的),而针对您的实际问题进行编码可能会以优雅的方式解决整个问题

如果我按照当前的情况推断您的问题,您只需要第一个元素的索引,它既不是
1
也不是
2
,实现起来很简单:

let categorize arr =
    try
        Array.findIndex (fun x -> not(x = 1 || x = 2)) arr
    with
        | :? System.Collections.Generic.KeyNotFoundException -> Array.length arr

// Usage
let x1 = categorize [|2;1;3|]    // returns 2
let x2 = categorize [|4;2;1;3|]  // returns 0
let x3 = categorize [|1;2;1|]    // returns 3
作为一些免费的好处,您可以获得与数组长度无关且绝对可读的代码。
这就是你需要的吗?

测试1 结论

  • 模式匹配不会基于->之后的值进行优化
  • 模式匹配可以根据结论1找到阵列分解的优化方法
  • 不完整的模式匹配总是抛出异常,因此添加一个通配符来捕获丢失的模式并显式抛出异常是无害的
  • 测试2 结论

  • 模式匹配从数组的开始到结束检查值。因此,没有找到优化的方法
  • 代码大小是最佳大小的2倍
  • 测试3 结论

  • 单案例活动模式编译为返回类型
  • 编译器有时会自动内联函数
  • 测试6 结论

  • 如果联合中有3个以上的事例,模式匹配将使用标记属性而不是is。(它也适用于多种情况下的活动模式。)
  • 通常,模式匹配会导致多个is,这会大大降低性能

  • 拆开看看?我的猜测是肯定的,尽管你的上一次更新(和标题更改)与你原来的问题有很大不同。也许最好开始一个新问题。参考:由Luc Maranget编写。Reflector生成
    数组>null
        F#
            let test1 x =
                match x with
                | [| 1; 2; 3 |] -> A
                | [| 1; 2; _ |] -> A
                | [| 1; _; _ |] -> A
        Decompiled C#
            if (x != null && x.Length == 3)
            {
                switch (x[0])
                {
                case 1:
                    switch (x[1])
                    {
                    case 2:
                        switch (x[2])
                        {
                        case 3:
                            return Program.MyType.A;
                        default:
                            return Program.MyType.A;
                        }
                        break;
                    default:
                        return Program.MyType.A;
                    }
                    break;
                }
            }
            throw new MatchFailureException(...);
        Decompiled IL
            Code size 107
    
        F#
            let test2 x =
                match x with
                | [| 1; 2; 3 |] -> A
                | [| _; 2; 3 |] -> B
                | [| _; _; 3 |] -> C
        Decompiled C#
            if (x != null && x.Length == 3)
            {
                switch (x[0])
                {
                case 1:
                    switch (x[1])
                    {
                    case 2:
                        switch (x[2])
                        {
                        case 3:
                            return Program.MyType.A;
                        default:
                            goto IL_49;
                        }
                        break;
                    default:
                        switch (x[2])
                        {
                        case 3:
                            break;
                        default:
                            goto IL_49;
                        }
                        break;
                    }
                    break;
                default:
                    switch (x[1])
                    {
                    case 2:
                        switch (x[2])
                        {
                        case 3:
                            return Program.MyType.B;
                        default:
                            goto IL_49;
                        }
                        break;
                    default:
                        switch (x[2])
                        {
                        case 3:
                            goto IL_58;
                        }
                        goto IL_49;
                    }
                    break;
                }
                IL_58:
                return Program.MyType.C;
            }
            IL_49:
            throw new MatchFailureException(...);
        Decompiled IL
            Code size 185
    
        F#
            let test3 x =
                match x with
                | [| 1; 2; 3 |] -> A
                | [| 1; 2; a |] when a <> 3 -> B
                | [| 1; 2; _ |] -> C
        Decompiled C#
            if (x != null && x.Length == 3)
            {
                switch (x[0])
                {
                case 1:
                    switch (x[1])
                    {
                    case 2:
                        switch (x[2])
                        {
                        case 3:
                            return Program.MyType.A;
                        default:
                            if (x[2] != 3)
                            {
                                int a = x[2];
                                return Program.MyType.B;
                            }
                            break;
                        }
                        break;
                    }
                    break;
                }
            }
            if (x != null && x.Length == 3)
            {
                switch (x[0])
                {
                case 1:
                    switch (x[1])
                    {
                    case 2:
                        return Program.MyType.C;
                    }
                    break;
                }
            }
            throw new MatchFailureException(...);
    
        F#
            let (| Is3 | IsNot3 |) x =
               if x = 3 then Is3 else IsNot3
            let test4 x =
                match x with
                | [| 1; 2; 3      |] -> A
                | [| 1; 2; Is3    |] -> B
                | [| 1; 2; IsNot3 |] -> C
                | [| 1; 2; _      |] -> D // This rule will never be matched.
        Decompiled C#
            if (x != null && x.Length == 3)
            {
                switch (x[0])
                {
                case 1:
                    switch (x[1])
                    {
                    case 2:
                        switch (x[2])
                        {
                        case 3:
                            return Program.MyType.A;
                        default:
                        {
                            FSharpChoice<Unit, Unit> fSharpChoice = Program.|Is3|IsNot3|(x[2]);
                            if (fSharpChoice is FSharpChoice<Unit, Unit>.Choice2Of2)
                            {
                                return Program.MyType.C;
                            }
                            return Program.MyType.B;
                        }
                        }
                        break;
                    }
                    break;
                }
            }
            throw new MatchFailureException(...);
    
        F#
            let (| Equal3 |) x =
               if x = 3 then Equal3 1 else Equal3 0 // Equivalent to "then 1 else 0"
            let test5 x =
                match x with
                | [| 1; 2; 3        |] -> A
                | [| 1; 2; Equal3 0 |] -> B
                | [| 1; 2; Equal3 1 |] -> C
                | [| 1; 2; _        |] -> D
        Decompiled C#
            if (x != null && x.Length == 3)
            {
                switch (x[0])
                {
                case 1:
                    switch (x[1])
                    {
                    case 2:
                        switch (x[2])
                        {
                        case 3:
                            return Program.MyType.A;
                        default:
                        {
                            int num = x[2];
                            switch ((num != 3) ? 0 : 1)
                            {
                            case 0:
                                return Program.MyType.B;
                            case 1:
                                return Program.MyType.C;
                            default:
                                return Program.MyType.D;
                            }
                            break;
                        }
                        }
                        break;
                    }
                    break;
                }
            }
            throw new MatchFailureException(...);
    
        F#
            let (| Partial3 | _ |) x =
               if x = 3 then Some (Partial3 true) else None // Equivalent to "then Some true"
            let test6 x =
                match x with
                | [| 1; 2; 3 |] -> A
                | [| 1; 2; Partial3 true |] -> B
                | [| 1; 2; Partial3 true |] -> C
        Decompiled C#
            if (x != null && x.Length == 3)
            {
                switch (x[0])
                {
                case 1:
                    switch (x[1])
                    {
                    case 2:
                        switch (x[2])
                        {
                        case 3:
                            return Program.MyType.A;
                        default:
                        {
                            FSharpOption<bool> fSharpOption = Program.|Partial3|_|(x[2]);
                            if (fSharpOption != null && fSharpOption.Value)
                            {
                                return Program.MyType.B;
                            }
                            break;
                        }
                        }
                        break;
                    }
                    break;
                }
            }
            if (x != null && x.Length == 3)
            {
                switch (x[0])
                {
                case 1:
                    switch (x[1])
                    {
                    case 2:
                    {
                        FSharpOption<bool> fSharpOption = Program.|Partial3|_|(x[2]);
                        if (fSharpOption != null && fSharpOption.Value)
                        {
                            return Program.MyType.C;
                        }
                        break;
                    }
                    }
                    break;
                }
            }
            throw new MatchFailureException(...);
    
        F#
            type MyOne =
                | AA
                | BB of int
                | CC
            type MyAnother =
                | AAA
                | BBB of int
                | CCC
                | DDD
            let test7a x =
                match x with
                | AA -> 2
            let test7b x =
                match x with
                | AAA -> 2
        Decompiled C#
            public static int test7a(Program.MyOne x)
            {
                if (x is Program.MyOne._AA)
                {
                    return 2;
                }
                throw new MatchFailureException(...);
            }
            public static int test7b(Program.MyAnother x)
            {
                if (x.Tag == 0)
                {
                    return 2;
                }
                throw new MatchFailureException(...);
            }