代码之家  ›  专栏  ›  技术社区  ›  SantiClaus

如何使用map.tryfind?

f#
  •  -1
  • SantiClaus  · 技术社区  · 8 年前

    当我运行下面的代码时,我得到一个错误。我正在使用map.tryfind,它不起作用。在控制台,我在下面有一条红线 familyinc.TryFind(tract) 以及下面的错误。

    let data =
        seq { for (state,work) in statecsv do
                let (family,income) = familyinc.TryFind(state) 
                let fam =
                    match fam with 
                    | Some x -> x
                    | None -> "Not a Record"
                let inc = 
                    match inc with 
                    | Some x -> x
                    | None -> "Not an income"
                yield (state,work,inc,fam)
        }
    

    错误:

    error FS0001: This expression was expected to have type
        ''a * 'b'    
    but here has type
        '(string * decimal) option'
    
    2 回复  |  直到 8 年前
        1
  •  3
  •   SantiClaus    8 年前

    编辑问题的答案: 问题与前一个相同,您在绑定选项时正在元组上进行模式匹配。你应该这样做:

    // Bind the whole option
    let result = familyinc.TryFind(state)
    
    // Pattern match on it
    match result with
    | Some (family , income) -> yield (state,work,family,income)
    | None -> yield (state,work,"Not a Record","Not an Income")
    

    当然你也可以 match familyinc.TryFind(tract) with ,这里不需要绑定到变量。


    问题是你是模式匹配的结果 Map.TryFind() 好像它会返回一个元组,但实际上它返回一个 option 因为可能找不到你要找的钥匙。

        2
  •  1
  •   s952163    8 年前

    在所有fp语言中,理解选项类型和模式匹配是必不可少的。事实上,这两个特性都使fp成为面向对象语言的一个更好的替代品。使用选项类型可以避免 null 异常,使用模式匹配可以解构值。在这种情况下,可以过滤掉不存在的键,并转换 option 结果为正常值:

    //create test dictionary
    let map1 = [("a",1); ("b",2);("c",3)] |> Map.ofList
    
    //list of keys, "d" doesn't exist in the dictionary/map
    let keys = ["a";"b";"d"]
    
    keys
    |> List.map (fun x -> map1.[x])
    //System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
    
    keys
    |> List.map (fun x -> map1.TryFind(x))
    //You get back a string option list, with the last element missing as the key "d" doesn't exist
    //val it : int option list = [Some 1; Some 2; None]
    
    //Method A: filter out the none existing items
    keys
    |> List.map (fun x -> map1.TryFind(x))
    |> List.choose id  //choose will get rid of the Nones and return the actual value, not the option. id is the identity function. 
    
    //Method B: replace the None with some default value, and also get rid of the option
    //Let's say you replace each non existing value with 999
    keys
    |> List.map (fun x -> map1.TryFind(x))
    |> List.map (Option.defaultValue 999)
    //val it : int list = [1; 2; 999]
    
    //In general if necessary you can always pattern match
    let myOption1 = Some "A"
    let myOption2 = None
    
    match myOption1 with
    | Some x -> x //try matching whatever is in myOption1 and returns the x portion of Some x, in this case "A"
    | None -> "No value"
    //val it : string = "A"
    
    match myOption2 with 
    | Some x -> x
    | None -> "No value" //since the value of myOption2 is None, Some x won't match, None will match, and return "No value"
    //val it : string = "No value"