【问题标题】:"Subsetting" a dictionary in F#F# 中的“子集”字典
【发布时间】:2016-04-12 03:24:54
【问题描述】:

我是 F# 的初学者,我正在尝试编写一个函数来对给定列表的字典进行子集化,并返回结果。

我试过了,但它不起作用。

let Subset (dict:Dictionary<'T,'U>) (sub_list:list<'T>) =
    let z = dict.Clear
    sub_list |> List.filter (fun k -> dict.ContainsKey k)
             |> List.map (fun k -> (k, dict.TryGetValue k) )
             |> List.iter (fun s -> z.Add s)

|> List.iter (fun s -> z.Add s);;
  --------------------------------------^^^

stdin(597,39): error FS0039: The field, constructor or member 'Add' is not defined

也许 F# 中有一个本地函数可以做到这一点?

谢谢

编辑 感谢@TheInnerLight 在下面的回答 你能再教育我一点,如果我想返回被修改的原始变量,我应该如何调整该函数? (当然可以从我们调用那个函数的地方开始,用一个临时变量调用它,然后重新分配)

【问题讨论】:

    标签: dictionary f# subset


    【解决方案1】:

    你写过:

    let z = dict.Clear
    

    zunit-&gt;unit 类型,但您正在调用 z.Add

    我怀疑你想写

    let subset (dict:Dictionary<'T,'U>) (sub_list:list<'T>) =
        let z = Dictionary<'T,'U>() // create new empty dictionary
        sub_list |> List.filter (fun k -> dict.ContainsKey k)
                 |> List.map (fun k -> (k, dict.[k]) )
                 |> List.iter (fun s -> z.Add s)
        z
    

    TryGetValue 将在 F# 中返回 bool*'U 类型的东西,我怀疑如果您已经通过 ContainsKey 过滤,您不希望这样,所以您可能想直接使用 dict.[k] 查找。

    请注意,Dictionary 是一个可变集合,因此如果您实际调用 dict.Clear(),它不会返回一个新的空字典,它会通过清除所有元素来改变现有字典。通常用于键值关系的不可变 F# 数据结构是 Map,有关您可以使用 Map 执行的操作,请参阅 https://msdn.microsoft.com/en-us/library/ee353880.aspx

    这是一个地图版本(这是我推荐的解决方案):

    let subset map subList =
        subList 
        |> List.choose (fun k -> Option.map (fun v -> k,v) (Map.tryFind k map))
        |> Map.ofList
    

    编辑(回应关于修改输入变量的问题编辑):

    可以在可变变量上使用破坏性更新运算符&lt;- 更新现有字典。

    选项 1:

    let mutable dict = Dictionary<Key,Value>() // replace this with initial dictionary
    let lst = [] // list to check against
    dict <- sublist dict lst
    

    同样,我的第一个函数可以更改为仅执行副作用(删除不需要的元素)。

    选项 2:

    let subset (d : System.Collections.Generic.Dictionary<'T,'U>) (sub_list : list<'T>) =
        sub_list 
        |> List.filter (d.ContainsKey >> not)
        |> List.iter (d.Remove >> ignore)
    

    对于 F# 初学者,我真的不推荐选项 1,我真的不推荐选项 2。

    函数式方法倾向于不可变值、纯函数等。这意味着您最好将函数视为定义数据转换,而不是定义要执行的指令列表。

    因为 F# 是一种多范式语言,所以在早期阶段很容易退回到命令式,但如果你强迫自己采用该语言的标准范式和习语,你可能会从学习新语言中获益最多即使这些成语一开始就觉得奇怪和不舒服。

    Maplist 这样的不可变数据结构在共享数据方面非常有效,并且提供了良好的时间复杂度,因此这些确实是在 F# 中工作时的首选集合。

    【讨论】:

    • @FaguiCurtain 没问题。已经更新了我的回答以回答您的第二个问题。
    • 但是如果我需要可变值,我应该使用字典而不是地图,对吗?您提出的带有 map 的解决方案中的函数是否会将字典作为参数?
    • @FaguiCurtain Dictionary 在 F# 中的主要用途可能是与类一起使用,尤其是在混合使用 C#/F# 时。 Map 有一个 comparison 要求,F# 中使用的大多数类型都会自动为您提供,但类不会。总有一种方法可以在不使用可变状态的情况下表达您的程序逻辑。一旦你掌握了这一点,你应该更好地了解什么时候应用一点可变性是有利的。你可以在这里阅读更多关于Maps(和Sets)的信息:en.wikibooks.org/wiki/F_Sharp_Programming/Sets_and_Maps
    • 你能举出一个或几个例子(或链接到)这样的逻辑。如何避免使用可变状态...
    • @FaguiCurtain 这个答案可以给你一个概述:stackoverflow.com/a/1020862/5438433这里有一个更深入的教程:fsharpforfunandprofit.com/posts/thinking-functionally-intro如果你喜欢看书,你可能想看看“Real世界函数式编程”
    猜你喜欢
    • 2018-08-13
    • 2012-04-20
    • 2018-08-03
    • 2016-02-13
    • 1970-01-01
    • 2019-02-13
    • 2011-03-17
    • 2012-10-03
    相关资源
    最近更新 更多