繁体   English   中英

模式匹配有多聪明?

[英]How smart is pattern match?

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

例如一个非常简单的案例

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|]

在这种情况下,编译器是否应用最少量的比较,通过识别例如第一种情况与第二种情况相同,除了第三种情况。

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

直接从反射器:

public static int categorize(int[] array)
{
    if ((array > null) && (array.Length == 3))
    {
        switch (array[0])
        {
            case 1:
                switch (array[1])
                {
                    case 1:
                        switch (array[2])
                        {
                            case 1:
                            case 2:
                                goto Label_005C;
                        }
                        goto Label_005A;

                    case 2:
                        switch (array[2])
                        {
                            case 1:
                            case 2:
                                goto Label_005C;
                        }
                        goto Label_005A;
                }
                goto Label_0042;

            case 2:
                switch (array[1])
                {
                    case 1:
                        switch (array[2])
                        {
                            case 1:
                            case 2:
                                goto Label_005C;
                        }
                        goto Label_005A;

                    case 2:
                        switch (array[2])
                        {
                            case 1:
                            case 2:
                                goto Label_005C;
                        }
                        goto Label_005A;
                }
                goto Label_0042;
        }
    }
    return 0;
Label_0042:
    return 1;
Label_005A:
    return 2;
Label_005C:
    return 3;
}

我没有看到任何效率低下的东西。

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

如果我按照目前的情况推断你的问题,你只需要第一个元素的索引,它既不是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

作为一些免费的好处,您将获得与数组长度无关并且绝对可读的代码。
这是你需要的吗?

你可以写:

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

    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

结论

  1. 模式匹配不会根据 - >后的值进行优化。
  2. 模式匹配能够在结论1下找到阵列分解的优化方法。
  3. 不完整模式匹配总是抛出异常,因此添加通配符来捕获缺少的模式并显式抛出异常是没有害处的。

测试2

    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

结论

  1. 模式匹配检查从数组开头到结尾的值。 因此无法找到优化的方法。
  2. 代码大小是最佳代码的2倍。

测试3

    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(...);

结论

  1. 编译器不够智能,无法通过Guard查看完整性/重复性。
  2. Guard使模式匹配产生奇怪的未优化代码。

测试4

    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(...);

结论

  1. 多个案例Active Patterns编译为FSharpChoice。
  2. 编译器能够检查活动模式的完整性/重复性,但是它无法将它们与正常模式进行比较。
  3. 未编译未到达的模式。

测试5

    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(...);

结论

  1. 单例活动模式编译为返回类型。
  2. 编译器有时会自动内联函数。

测试6

    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(...);

结论

  1. 部分活动模式编译为FSharpOption。
  2. 编译器无法检查部分活动模式的完整性/重复性。

测试7

    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(...);
        }

结论

  1. 如果联合中有超过3个案例,模式匹配将使用Tag属性而不是is。 (它也适用于多种情况的活动模式。)
  2. 模式匹配通常会导致多重性能大大降低性能。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM