我有两个列表listA
和listB
,如果true
包含listB
中的任何元素,我想返回listA
。
let listA = ["A";"B";"C"]
let listB = ["D";"E";"A"]
在这种情况下应该返回true
。我觉得这应该很容易解决,我在某处遗漏了一些基本的东西。
例如,为什么我不能这样做?
let testIntersect = for elem in listA do List.exists (fun x -> x = elem) listB
答案 0 :(得分:3)
您可以使用的一个函数是List.except
,它尚未记录(!),但可以在几年前合并的this pull request中看到。您可能会像这样使用它:
let testIntersect a b =
let b' = b |> List.except a
// If b' is shorter than b, then b contained at least one element of a
List.length b' < List.length b
但是,这会在列表B中运行三次,一次执行except
算法,一次执行length
次调用。所以另一种方法可能是做你做的事情,但把列表A变成一个集合,以便exists
调用不会是O(N):
let testIntersect a b =
let setA = a |> Set.ofList
match b |> List.tryFind (fun x -> setA |> Set.contains x) with
| Some _ -> true
| None -> false
我使用tryFind
的原因是因为如果谓词与列表中的任何项匹配,List.find
会抛出异常。
修改:更好的方法是使用我暂时忘记的List.exists
(感谢Honza Brestan提醒我):
let testIntersect a b =
let setA = a |> Set.ofList
b |> List.exists (fun x -> setA |> Set.contains x)
当然,这正是您最初希望在testIntersect
代码示例中执行的操作。唯一的区别是您在代码示例中使用了for ... in
语法,这种方法不起作用。在F#中,对于返回for
的表达式,unit
循环独占(因此可能有副作用)。如果您想返回一个值,for
循环不会这样做。因此,使用做返回值的函数(如List.exists
)就是您要采用的方法。
答案 1 :(得分:3)
您不能编写类似于示例代码的内容,因为普通for
没有返回结果,它只是为表达式的副作用计算表达式。您可以用for
理解来编写代码:
let testIntersect listA listB =
[for elem in listA do yield List.exists (fun x -> x = elem) listB]
当然,这会返回bool list
而不是单bool
。
val testIntersect : listA:seq<'a> -> listB:'a list -> bool list when 'a : equality let listA = ["A";"B";"C"] let listB = ["D";"E";"A"] testIntersect listA listB val it : bool list = [true; false; false]
因此,我们可以使用List.exists
函数确保true
至少出现一次:
let testIntersect listA listB =
[for elem in listA do yield List.exists (fun x -> x = elem) listB]
|> List.exists id
val testIntersect : listA:seq<'a> -> listB:'a list -> bool list when 'a : equality val listA : string list = ["A"; "B"; "C"] val listB : string list = ["D"; "E"; "A"] val it : bool = false
使用List
解决此问题的效率非常低,但使用Set
会更好。使用Set
,您可以在 O(log N * log M)时间内计算交点,而不是 O(N * M)。
let testSetIntersect listA listB =
Set.intersect (Set.ofList listA) (Set.ofList listB)
|> Set.isEmpty
|> not
答案 2 :(得分:2)
let testIntersect listA listB =
(Set.ofList listA) - (Set.ofList listB) |> Set.isEmpty |> not