我正在学习处理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)列表”
感谢您的帮助!
答案 0 :(得分:3)
首先让我们看看你的代码并注释所有类型:
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' -> bool) -> seq<'a> -> 'a
方法代替手动过滤:
let findAge l name = l |> Seq.find (fun (a, b) -> a = name) |> snd
或者您可以尝试使用其他数据结构,例如Map<'key, 'value>
:
> 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
答案 1 :(得分:2)
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
类型的形式,可以采用两个值:None
和Some(x)
。它用于有时不会产生有用结果的函数。
签名为:tryFind : ('T -> bool) -> 'T list -> 'T option
,其中'T
为项类型,('T -> bool)
为谓词函数类型。
在这种情况下,它将搜索target
列表,查找元组,其中第一个元素(y
)等于外部函数中的变量x
。
然后我们有List.pick mapper list
函数,它将mapper
- 函数应用于每个函数,直到返回的第一个结果不是None
。
此函数不会返回option
值,但如果找不到任何项,则会抛出异常。此函数的option
变体名为List.tryPick
。
签名为:pick : ('T -> 'U option) -> 'T list -> 'U
,其中'T
为项类型,'U
为结果类型,('T -> 'U option)
为映射函数类型。
在这种情况下,它将遍历source
- 列表,在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
(xs
和ys
是编写列表或项目序列的常用方法。
答案 2 :(得分:0)
像fst一样使用这个(下面)。这样您就可以访问所有值。
这是来自F#interactive
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