【问题标题】:Concisely creating an IDictionary<_,obj>简洁地创建一个 IDictionary<_,obj>
【发布时间】:2011-11-16 11:56:00
【问题描述】:

有没有更短的方法来创建IDictionary&lt;_,obj&gt;,可能不需要对每个值进行装箱?这就是我所拥有的。

let values =
  [ "a", box 1
    "b", box "foo"
    "c", box true ]
  |> dict

Dictionary&lt;_,obj&gt;.Add 可以在没有装箱的情况下调用,但我想不出比我拥有的更短的使用方法。

除了定义一个装箱操作符之外,我还希望有别的东西。

编辑

根据 Brian 的建议,这是一种方法,但它有其自身的问题。

let values =
  Seq.zip ["a"; "b"; "c"] ([1; "foo"; true] : obj list) |> dict

【问题讨论】:

    标签: f#


    【解决方案1】:

    这是一个解决方案,遵循 kvb 的建议(可能是迄今为止最简洁、最清晰的):

    let inline (=>) a b = a, box b
    
    let values =
      [ "a" => 1
        "b" => "foo"
        "c" => true ]
      |> dict
    

    【讨论】:

    • 是的,这很好,请随意切换您接受的答案!
    • 我正在给 kvb 一个发布它的机会。反正我两天都接受不了自己的答案。
    【解决方案2】:

    这是我能做的最巧妙的事情。它比你的拳击版本有更多的字符,但可能感觉不那么脏。请注意,^ 是右关联的(它是从 ocaml 继承的字符串 concat 运算符),它可以像:: 一样工作,并且它比, 具有更高的优先级,这就是为什么需要在元组周围加上括号的原因.

    let inline (^+) (x1:'a,x2:'b) (xl:('a*obj) list) = 
        (x1,box x2)::xl
    
    let values =
      ("a", 1) ^+  ("b", "foo") ^+ ("c", true) ^+ []
      |> dict
    

    【讨论】:

    • 有没有比, 更低的优先级,所以我们可以省略括号?
    • 我能找到的唯一允许优先级低于 , 的自定义运算符是 := (并且也恰好是右关联的)但这已经被 ref 设置(和以::= 开头的变体都是无效的,它必须完全是:=)
    • 这是一个不错的解决方案,而且可能会达到预期效果。谢谢!
    • 好的,谢谢!我记得前一阵子为此苦苦挣扎,所以当你发布这个问题时,我的第一反应是“不!”。这个巧妙的解决方案只是灵感的突然闪现(疯狂?)。
    【解决方案3】:

    我在 FsSql 中遇到了类似的问题,我只是将装箱隐藏在一个函数中:

    let inline T (a,b) = a, box b
    let values = dict [T("a",1); T("b","foo"); T("c",true)]
    

    【讨论】:

    • 这需要与 box 相同数量的额外字符 (T())。实际上,更短的是你计算box之后的空格。
    • @Daniel:这是关于打高尔夫球的吗?我只是觉得这比装箱每个值更方便。
    • 并非如此。我只是想知道有什么好处,因为按键几乎相同。
    • @Daniel:更少的击键!=更好。我觉得这在心理上更容易,当然是 YMMV。
    • @Mauricio,你不是说less keystrokes &lt;&gt; better吗?
    【解决方案4】:

    这是另一个“解决方案”,灵感来自 Brian 的建议,但它使用反射,因此需要时间和安全成本。

    let unboxPair (pair:obj) =
        let ty = pair.GetType()
        let x = ty.GetProperty("Item1").GetValue(pair,null) :?> string
        let y = ty.GetProperty("Item2").GetValue(pair,null)
        x,y
    
    let unboxPairs (pairs:obj list) =
      pairs |> List.map unboxPair  
    
    let values =
      unboxPairs
        ["a", 1
         "b", "foo"
         "c", true]
      |> dict
    

    【讨论】:

      【解决方案5】:

      斯蒂芬想法的变体:

      open System
      open System.Collections.Generic
      
      type Dictionary<'a,'b> with
        member this.Add([<ParamArray>] args:obj[]) =
          match args.Length with
          | n when n % 2 = 0 ->
            for i in 1..2..(n-1) do
              this.Add(unbox args.[i-1], unbox args.[i])
          | _ -> invalidArg "args" "even number of elements required"
      
      let d = Dictionary<string,obj>()
      d.Add(
        "a", 1,
        "b", "foo",
        "c", true
      )
      

      【讨论】:

        【解决方案6】:

        另一种解决方案,只需在Dictionary&lt;'a,'b&gt;上定义一堆重载的扩展成员:

        open System.Collections.Generic
        type Dictionary<'a,'b> with
            member this.Add(x1,y1,x2,y2) =
                this.Add(x1,y1)
                this.Add(x2,y2)
            member this.Add(x1,y1,x2,y2,x3,y3) =
                this.Add(x1,y1,x2,y2)
                this.Add(x3,y3)
            member this.Add(x1,y1,x2,y2,x3,y3,x4,y4) =
                this.Add(x1,y1,x2,y2,x3,y3)
                this.Add(x4,y4)
            member this.Add(x1,y1,x2,y2,x3,y3,x4,y4,x5,y5) =
                this.Add(x1,y1,x2,y2,x3,y3,x4,y4)
                this.Add(x5,y5)
            member this.Add(x1,y1,x2,y2,x3,y3,x4,y4,x5,y5,x6,y6) =
                this.Add(x1,y1,x2,y2,x3,y3,x4,y4,x5,y5)
                this.Add(x6,y6)
            //etc.
        
        let values = 
            let d = Dictionary<_,obj>()
            d.Add("a", 1, 
                  "b", "foo", 
                  "c", true)
            d
        

        当然,values 这里不是像你的问题一样是不可变的,但我相信你可以在这个目标中采用相同的策略。

        【讨论】:

        • 您可以使用ParamArray 代替重载。我发布了一个带有示例的答案。
        • 是的,那么这是这个答案和我的反思答案之间的混合体,因为你失去了键的类型安全性。
        【解决方案7】:
        let v : (string*obj) list = [...]
        let values = dict v
        

        是一种方式,列表文字左侧的类型签名将自动向上转换每个元素。

        【讨论】:

        • 它给出了错误expected to have type obj。这是我尝试的第一件事。
        • 虽然这个技巧适用于 obj list,但它不适用于 (string*obj) list,因为 string*int 不是 string*obj 的子类(并且 F# 不支持任何种协方差,尽管这在 C# 中也不起作用,因为泛型协方差仅适用于 .NET 中的接口和委托,并且 Tuple 系列类不实现任何类型的 ITUple 接口)。
        • 啊,哎呀,这就是我在远离机器的手机上输入答案的结果:)
        猜你喜欢
        • 1970-01-01
        • 2010-10-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-11
        • 1970-01-01
        相关资源
        最近更新 更多