从F#中的n-ary树中提取叶子路径

时间:2008-11-12 10:42:23

标签: algorithm f# n-ary-tree

受到this question的启发,我想用最新的ponder this challenge尝试使用F#

我的方法可能完全偏离正轨,但在解决这个问题的过程中,我试图得到数字0-9的所有排列的列表。

我正在寻找使用像这样的n-ary树来解决它:

type Node = 
    | Branch of (int * Node list)
    | Leaf of int

我对自己很满意,因为我已经设法弄清楚如何生成我想要的树。

我现在的问题是我无法弄清楚如何遍历这棵树并将每个叶子的'path'作为int提取出来。令我困惑的事情是我需要匹配各个节点,但我的“外部”功能需要采用节点列表。

我当前的尝试几乎做了正确的事情,除了它返回所有路径的总和......

let test = Branch(3, [Branch(2, [Leaf(1)]);Branch(1, [Leaf(2)])])

let rec visitor lst acc = 
    let inner n = 
        match n with
        | Leaf(h) -> acc * 10 + h
        | Branch(h, t) -> visitor t (acc * 10 + h)
    List.map inner lst |> List.sum

visitor [test] 0 //-> gives 633 (which is 321 + 312)

我甚至不确定这是尾递归的。

(非常欢迎您提出另一种寻找排列的解决方案,但我仍然对这个特定问题的解决方案感兴趣)

编辑:我在F#here中发布了一个通用排列算法。

2 个答案:

答案 0 :(得分:5)

关于列表遍历的问题 - 你可以从编写一个返回表示路径的列表的函数开始 - 我觉得这很容易,以后会很容易将它变成一个返回数字的函数。

这个列表作为第一个参数(到目前为止的路径)和一个树并返回一个列表> type - 来自当前分支的所有可能路径。

let rec visitor lst tree = 
  match tree with
  | Branch(n, sub) -> List.collect (visitor (n::lst)) sub
  | Leaf(n) -> [List.rev (n::lst)]

// For example...
> let tr = Branch(1, [Leaf(3); Branch(2, [Leaf(4); Leaf(5)] )]);;
> visitor [] tr;;
val it : int list list = [[1; 3]; [1; 2; 4]; [1; 2; 5]]

在'Leaf'的情况下,我们只需将当前数字添加到列表中,并将结果作为包含单个列表的列表返回(我们必须先将其反转,因为我们在开头添加数字)。 在'Branch'的情况下,我们在列表中添加'n'并递归调用访问者来处理当前分支的所有子节点。这将返回一堆列表,我们使用'map_concat'将它们转换为包含当前分支中所有可用路径的单个列表。

现在,您可以重写此内容以返回整数列表:

let rec visitor2 lst tree = 
  match tree with
  | Branch(n, sub) -> List.collect (visitor2 (lst * 10 + n)) sub
  | Leaf(n) -> [lst * 10 + n]

// For example...  
> visitor2 0 tr;;
val it : int list = [13; 124; 125]  

我们现在计算数字,而不是连接列表。

答案 1 :(得分:2)

关于懒惰 - 你可以使用F#“seq”类型而不是“list”类型来使这个懒惰。这是一个例子:

let rec visitor2 lst tree =
  match tree with
  | Branch(n, sub) -> Seq.map_concat (visitor2 (lst * 10 + n)) sub
  | Leaf(n) ->
      seq { do printfn "--yielding: %d" (lst * 10 + n)
            yield lst * 10 + n };;

“seq”事物是一个序列表达式,它表示一个惰性值流。我在代码中添加了“printfn”,因此我们可以跟踪事情的执行情况:

> visitor2 0 tr |> Seq.take 2;;
--yielding: 13
--yielding: 124
val it : seq<int> = seq [13; 124]

你可以使用像Seq.first这样的东西来找到代表结果的第一个值。