这伤害了我的大脑!
我想在树结构上进行递归,并将与某个过滤器匹配的所有实例收集到一个列表中。
这是一个示例树结构
type Tree =
| Node of int * Tree list
这是一个测试样本树:
let test =
Node((1,
[Node(2,
[Node(3,[]);
Node(3,[])]);
Node(3,[])]))
使用和int值为3的节点收集和过滤应该给出如下输出:
[Node(3,[]);Node(3,[]);Node(3,[])]
答案 0 :(得分:6)
以下递归函数应该可以解决这个问题:
// The 'f' parameter is a predicate specifying
// whether element should be included in the list or not
let rec collect f (Node(n, children) as node) =
// Process recursively all children
let rest = children |> List.collect (collect f)
// Add the current element to the front (in case we want to)
if (f n) then node::rest else rest
// Sample usage
let nodes = collect (fun n -> n%3 = 0) tree
函数List.collect
将提供的函数应用于所有元素
list children
- 每次调用都会返回一个元素列表和List.collect
将所有返回的列表连接成一个。
或者你可以写(这个maay帮助理解代码是如何工作的):
let rest =
children |> List.map (fun n -> collect f n)
|> List.concat
同样的事情也可以使用列表推导来编写:
let rec collect f (Node(n, children) as node) =
[ for m in children do
// add all returned elements to the result
yield! collect f m
// add the current node if the predicate returns true
if (f n) then yield node ]
编辑:更新代码以返回节点,如kvb所示。
BTW:到目前为止,显示一些您尝试编写的代码通常是个好主意。这有助于人们了解您不理解的部分,因此您将获得更多有用的答案(并且它也被视为礼貌)答案 1 :(得分:3)
更复杂的尾递归解决方案。
let filterTree (t : Tree) (predicate : int -> bool) =
let rec filter acc = function
| (Node(i, []) as n)::tail ->
if predicate i then filter (n::acc) tail
else filter acc tail
| (Node(i, child) as n)::tail ->
if predicate i then filter (n::acc) (tail @ child)
else filter acc (tail @ child)
| [] -> acc
filter [] [t]
答案 2 :(得分:2)
仅仅显示F# Sequences Expression
的用法(可能不是最佳方法,我认为Tomas的解决方案可能更好):
type Tree =
| Node of int * Tree list
let rec filterTree (t : Tree) (predicate : int -> bool) =
seq {
match t with
| Node(i, tl) ->
if predicate i then yield t
for tree in tl do
yield! filterTree tree predicate
}
let test = Node (1, [ Node(2, [ Node(3,[]); Node(3,[]) ]); Node(3,[]) ])
printfn "%A" (filterTree test (fun x -> x = 3))
答案 3 :(得分:1)
当我的大脑受伤,因为它被困在一棵树上,我尽可能简单明了地说出我想要的东西:
一种直接的方法是列出树的所有节点,然后过滤谓词。
type 'info tree = Node of 'info * 'info tree list
let rec visit = function
| Node( info, [] ) as node -> [ node ]
| Node( info, children ) as node -> node :: List.collect visit children
let filter predicate tree =
visit tree
|> List.filter (fun (Node(info,_)) -> predicate info)
这是针对OP的样本数据运行的树过滤器:
let result = filter (fun info -> info = 3) test
让我感到惊讶的一件事是代码适用于任何具有适当谓词的信息类型:
let test2 =
Node(("One",
[Node("Two",
[Node("Three",[Node("Five",[]);Node("Three",[])]);
Node("Three",[])]);
Node("Three",[])]))
let res2 = filter (fun info -> info = "Three") test2
或者,如果您想列出信息而不是子树,则代码非常简单:
let rec visit = function
| Node( info, [] ) -> [ info ]
| Node( info, children ) -> info :: List.collect visit children
let filter predicate tree =
visit tree
|> List.filter predicate
支持相同的查询,但只返回'信息记录,而不是树结构:
let result = filter (fun info -> info = 3) test
> val result : int list = [3; 3; 3; 3]
答案 4 :(得分:0)
Tomas的方法看起来很标准,但与你的例子并不完全一致。如果您确实需要匹配节点列表而不是值,则应该可以:
let rec filter f (Node(i,l) as t) =
let rest = List.collect (filter f) l
if f t then t::rest
else rest
let filtered = filter (fun (Node(i,_)) -> i=3) test
答案 5 :(得分:0)
这是一个过度设计的解决方案,但它显示了对Partial Active Patterns的关注点的分离。这不是部分活动模式的最佳示例,但它仍然是一项有趣的练习。匹配语句按顺序进行评估。
let (|EqualThree|_|) = function
| Node(i, _) as n when i = 3 -> Some n
| _ -> None
let (|HasChildren|_|) = function
| Node(_, []) -> None
| Node(_, children) as n -> Some children
| _ -> None
let filterTree3 (t : Tree) (predicate : int -> bool) =
let rec filter acc = function
| EqualThree(n)::tail & HasChildren(c)::_ -> filter (n::acc) (tail @ c)
| EqualThree(n)::tail -> filter (n::acc) tail
| HasChildren(c)::tail -> filter acc (tail @ c)
| _::tail -> filter acc tail
| [] -> acc
filter [] [t]