如果 Map 包含键 return 值,否则 return 0

If Map contains key return value, otherwise return 0

我是 F# 的新手,想按照标题所说的去做,但我不断收到这些类型错误。

我知道这行得通:

> let rec validString2 =
  function
  | a -> Map.find a
> validString2 "x" (Map.ofList [("x", 5)]);;
>val it: int = 5

但我想要 return Map.find a 只有 如果 a 在地图上,否则我想要 return 0。当我尝试这个时:

let rec validString =
  function
  | a -> if (Map.containsKey a) then Map.find a else 0

我收到的错误消息(以及 Map.containsKey a 下的红色下划线):

This expression was expected to have type
    'bool'    
but here has type
    'Map<'a,'b> -> bool'

如果我尝试也会发生同样的情况:

let validString =
  function
  | Some x -> x
  | None -> 0

let rec evalValidString =
  function
  | V a -> validString (Map.tryFind a)

我似乎误解了地图类型的工作原理。我不明白 bool 与 Map<'a, 'b> -> bool 有何不同。地图函数 return 是一个布尔值,我应该可以对其求值。

我认为这里的问题是您必须指定要搜索的地图。在下面的函数中,我将其命名为 myMap:

let myLookup (key : string) myMap =
    myMap
        |> Map.tryFind key         // does the key exist in myMap?
        |> Option.defaultValue 0   // if not, return 0

请注意 myMapMap<string, int> 类型的值。然后您可以按如下方式使用此函数:

let aMap = Map.ofList [("x", 5)]
let xValue = myLookup "x" aMap
let yValue = myLookup "y" aMap
printfn "%A" (xValue, yValue)   // 5, 0

我知道这个问题已经有一个公认的答案,但我认为一些进一步的评论也可能会增加您对 F# 的学习:

let rec validString2 =
    function
    | a -> Map.find a

validString2 "x" (Map.ofList [("x", 5)]);;

val it: int = 5
  1. rec 表示这是一个递归函数,但函数体中没有 调用validString。它不是递归函数,因此不需要 rec
  let validString2' = function | a -> Map.find a
  1. 现在使用函数是一种模式匹配,它使 validString2 的最后一个参数隐含。可以用 match 明确说明吗?
  let validString2'' a = match a with a -> Map.find a
  1. 是的!因为,无论它是什么,都在 a 中匹配,因此只需要 one 分支。即根本不需要模式匹配:
  let validString2''' a = Map.find a
  1. 但是 Map.find 需要 两个 个参数,但这里只有一个 (a)。所以 validString2 是一个 部分应用的 函数,仍在等待第二个参数,即要在 a 中找到的实际映射。这解释了 validString 中的错误因为 Map.containsMap.find 都需要第二个参数,并且作为部分应用函数,它们具有类型签名 'Map<'a,'b> -> bool'(Map.contains k) 和 'Map<'a,'b> -> 'b(Map.find k 它还不知道 'b 分别是 int)。因此你的错误信息。

  2. 最后,您可以将您的原始解决方案演变成:

  let validString' k m = if Map.contains k m then Map.find k m else 0

因此在这种情况下不需要模式匹配或部分应用函数,但希望这有助于您在未来的使用中理解这些。

更新:

  1. 解决方案 (4) 需要对数据进行两次传递,第一次是 Map.contains - O(log(N)) - 第二次是 Map.find - 也是 O(log(N)) 。我们可以单程通过吗?
  let validString'' k m = if Map.contains k m then m[k] else 0  
  1. 但是这两个操作仍然是O(log(N)),因为m[k]而不是 O(1),所以这是仍然 2*O(log(N))。我们能做得更好吗?这是您查看 try... 集合函数变体的地方。在这种情况下,您需要 map.tryFind 其中 returns 一个选项,带有 Some valueNone - 如果密钥不存在。这是一个单程O(log(N))解决方案:
    let validString''' k m = 
        match Map.tryFind k m with
        | Some v -> v 
        | None -> 0
  1. 这个模式匹配表达式有自己的收集功能Option.defaultValue,这就引出了前面给出的解决方案和标记的正确答案。