【问题标题】:Pattern combining type test and literal结合类型测试和文字的模式
【发布时间】:2012-08-12 22:23:51
【问题描述】:

this question 中的活动模式在升级到 VS 2012 RTM 后无法编译。它提供了一种进行类型测试并匹配单个模式中的文字的方法。例如:

let (|Value|_|) value = 
  match box value with
  | :? 'T as x -> Some x
  | _ -> None

let getValue (name: string) (r: IDataReader) =
  match r.[name] with
  | null | :? DBNull | Value "" -> Unchecked.defaultof<_>
  | v -> unbox v

这可以在没有活动模式的情况下完成吗?我意识到可以使用when 防护(:? string as s when s = ""),但它不能与其他模式结合使用。

【问题讨论】:

    标签: f# f#-3.0 active-pattern guard-clause


    【解决方案1】:

    您应该能够使用参数化的活动模式:

    let (|Value|_|) v x = 
        if unbox x = v then 
            Some() 
        else None
    

    用法应该和你现在的完全一样。

    编辑

    虽然我不知道重大更改是否是故意的,但我认为通常应该避免使用与输入类型无关的通用返回类型的活动模式。当与类型推断结合使用时,它们可以轻松掩盖细微的错误。考虑以下示例,使用您原来的 (|Value|_|) 模式:

    match [1] with
    | Value [_] -> "Singleton"
    | _ -> "Huh?"
    

    这似乎不是你真正尝试过的——顾名思义,Value 只能与文字一起使用;参数化的活动模式正好支持这种情况。

    【讨论】:

    • 您的解决方法有些不同,但它提供了很好的方向。请参阅我的答案以获取兼容的变体。
    • 好办法。
    • 我认为你的反例有点像稻草人。 (|Value|_|) 或其返回类型中的任何泛型函数都要求您注意推断出的类型。您的示例更多地说明了+,即它的操作数被推断为int,除非内联。
    • 您是否也认为应该禁止以下活动模式? let (|X|) x = unbox x 这是另一个在 2.0 中有效但在 3.0 中无效 的示例。
    • 我改变了我的例子 - 我希望这能更清楚地说明我认为有问题的错误类型。
    【解决方案2】:

    kvb 的变体(它不会做完全相同的事情,因为它假设类型测试成功)可以修改以产生类似的模式:

    let (|Value|_|) x value =
      match box value with
      | :? 'T as y when x = y -> Some()
      | _ -> None
    

    但是,存在细微的性能差异。原来的活动模式转化为:

    public static FSharpOption<T> |Value|_|<a, T>(a value)
    {
        object obj = value;
        if (!LanguagePrimitives.IntrinsicFunctions.TypeTestGeneric<T>(obj))
        {
            return null;
        }
        return FSharpOption<T>.Some((T)((object)obj));
    }
    

    也就是说,它会进行类型测试和强制转换。它的用法 (match x with Value "" -&gt; ...) 转换为:

    FSharpOption<string> fSharpOption = MyModule.|Value|_|<object, string>(obj);
    if (fSharpOption != null && string.Equals(fSharpOption.Value, ""))
    {
        ...
    }
    

    最值得注意的是,从模式返回的类型化值使用典型的编译器转换模式匹配(string.Equals 用于字符串)。

    更新后的模式转换为:

    public static FSharpOption<Unit> |Value|_|<T, a>(T x, a value)
    {
        object obj = value;
        if (LanguagePrimitives.IntrinsicFunctions.TypeTestGeneric<T>(obj))
        {
            T y = (T)((object)obj);
            T y3 = y;
            if (LanguagePrimitives.HashCompare.GenericEqualityIntrinsic<T>(x, y3))
            {
                T y2 = (T)((object)obj);
                return FSharpOption<Unit>.Some(null);
            }
        }
        return null;
    }
    

    它使用泛型相等性并且比匹配文字效率低。用法稍微简单一些,因为模式中包含了相等性:

    FSharpOption<Unit> fSharpOption = MyModule.|Value|_|<string, object>("", obj);
    if (fSharpOption != null)
    {
        ...
    }
    

    无论如何,它有效。但我更喜欢原版。

    【讨论】:

    • 这可行,但它也不允许您使用嵌套模式(因为参数化活动模式的参数是评估表达式,而不是匹配的模式)。
    • 对。这是不同相等语义的根本原因,但你说得更简洁。
    猜你喜欢
    • 1970-01-01
    • 2019-05-10
    • 2011-10-04
    • 2013-03-08
    • 1970-01-01
    • 2012-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多