【问题标题】:Returning first value of list of tuples返回元组列表的第一个值
【发布时间】:2010-12-22 20:37:58
【问题描述】:

我正在学习在 F# 中处理列表和元组,但出现了一个问题。我有两个列表:一个名字和一个名字,年龄。


let namesToFind = [ "john", "andrea" ]
let namesAndAges = [ ("john", 10); ("andrea", 15) ]

我正在尝试创建一个函数,该函数将返回在给定 namesToFind 的 namesAndAges 中找到的第一个年龄。只是第一个。

到目前为止,我有以下代码返回整个元组(“john”,10)。


let findInList source target = 
            let itemFound = seq { for n in source do
                                    yield target |> List.filter (fun (x,y) -> x = n)  }                                
                                |> Seq.head    
            itemFound

我尝试在返回语句中使用 fst(),但它没有编译并给我“这个表达式应该有类型 'a * 'b 但这里有类型 ('c * 'd) 列表”

感谢您的帮助!

【问题讨论】:

    标签: list f# tuples


    【解决方案1】:

    Collections.List 模块中有很多功能可以使用。由于 F# 中没有 break 或真正的 return 语句,因此通常最好使用一些搜索函数,或者编写递归循环函数。这是一个例子:

    let namesToFind = [ "john"; "andrea" ]
    let namesAndAges = [ "john", 10; "andrea", 15 ]
    
    let findInList source target =
      List.pick (fun x -> List.tryFind (fun (y,_) -> x = y) target) source
    
    findInList namesToFind namesAndAges
    

    findInList 函数由Collections.List 模块中的两个函数组成。

    • 首先我们有List.tryFind predicate list 函数,它返回给定谓词函数返回true 的第一项。

      结果是option 类型的形式,它可以取两个值:NoneSome(x)。它用于有时没有给出有用结果的函数。

      签名是:tryFind : ('T -> bool) -> 'T list -> 'T option,其中'T是项目类型,('T -> bool)是谓词函数类型。

      在这种情况下,它将搜索 target 列表,查找第一个元素 (y) 等于外部函数中的变量 x 的元组。

    • 然后我们有List.pick mapper list函数,它将mapper函数应用于每个函数,直到返回第一个不是None的结果。

      此函数不会返回option 值,但如果未找到任何项目,则会抛出异常。这个函数还有一个option-variant,名为List.tryPick

      签名为:pick : ('T -> 'U option) -> 'T list -> 'U,其中'T为项目类型,'U为结果类型,('T -> 'U option)为映射函数类型。

      在这种情况下,它将遍历source-list,在target 数组中(通过List.tryFind)为每个匹配项寻找匹配项,并在第一个匹配项处停止。


    如果您想明确地编写循环,它的外观如下:

    let findInList source target =
      let rec loop names =
        match names with
        | (name1::xs) -> // Look at the current item in the
                         // source list, and see if there are
                         // any matches in the target list.
            let rec loop2 tuples =
              match tuples with
              | ((name2,age)::ys) ->  // Look at the current tuple in
                                      // the target list, and see if
                                      // it matches the current item.
                  if name1 = name2 then
                    Some (name2, age) // Found a  match!
                  else
                    loop2 ys          // Nothing yet; Continue looking.
              | [] -> None            // No more items, return "nothing"
            match loop2 target with           // Start the loop
            | Some (name, age) -> (name, age) // Found a match!
            | None -> loop rest               // Nothing yet; Continue looking.
        | [] -> failwith "No name found" // No more items.
    
      // Start the loop
      loop source
    

    xsys 是编写列表或项目序列的常用方法)

    【讨论】:

      【解决方案2】:

      首先让我们看一下您的代码并注释所有类型:

      let findInList source target = 
          let itemFound =
              seq {
                  for n in source do
                      yield target |> List.filter (fun (x,y) -> x = n) }                                
              |> Seq.head    
          itemFound
      

      语句yield List.Filter ... 表示您正在创建一系列列表:seq<list<'a * 'b>>

      语句Seq.head 获取列表序列中的第一个元素:list<'a * 'b>

      所以整个函数返回一个list<'a * 'b>,这显然不是你的函数的正确类型。我想你打算写这样的东西:

      let findInList source target = 
          let itemFound =
              target                               // list<'a * 'b>
              |> List.filter (fun (x,y) -> x = n)  // list<'a * 'b>
              |> Seq.head                          // 'a * 'b
          itemFound                                // function returns 'a * 'b
      

      您可以通过多种方式获得所需的结果。您的代码已经完成了一半。代替手动过滤,我建议使用内置的val Seq.find : (a' -&gt; bool) -&gt; seq&lt;'a&gt; -&gt; 'a 方法:

      let findAge l name = l |> Seq.find (fun (a, b) -> a = name) |> snd
      

      或者您可以尝试使用不同的数据结构,例如 Map&lt;'key, 'value&gt;

      > let namesAndAges = [ ("john", 10); ("andrea", 15) ] |> Map.ofList;;
      
      val namesAndAges : Map<string,int> = map [("andrea", 15); ("john", 10)]
      
      > namesAndAges.["john"];;
      val it : int = 10
      

      如果你想手写,那么用你的 seq 表达式试试这个:

      let findInList source target = 
          seq {
              for (x, y) in source do
                  if x = target then
                      yield y}
          |> Seq.head
      

      【讨论】:

        【解决方案3】:

        像 fst 一样使用这个(下)。这样您就可以访问所有值。

        这是来自 F# 交互

        let a = ((1,2), (3,4));
        let b = snd (fst a);;
        
        //interactive output below.
        val a : (int * int) * (int * int) = ((1, 2), (3, 4))
        val b : int = 2
        

        【讨论】:

          猜你喜欢
          • 2021-06-29
          • 2013-11-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-03
          • 2013-03-21
          相关资源
          最近更新 更多