Оптимизация хвостового вызова F # с 2 рекурсивными вызовами?

Когда я писал эту функцию, я знал, что не получу оптимизацию хвостового вызова. Я до сих пор не придумал хороший способ справиться с этим и надеялся, что кто-то другой может предложить предложения.

У меня есть дерево:

type Heap<'a> =
| E
| T of int * 'a * Heap<'a> * Heap<'a> 

И я хочу посчитать, сколько узлов в нем:

let count h =
    let rec count' h acc =
        match h with 
        | E -> 0 + acc
        | T(_, value, leftChild, rightChild) ->
            let acc = 1 + acc
            (count' leftChild acc) + (count' rightChild acc)

    count' h 0

Это не оптимизировано из-за добавления счетчиков для дочерних узлов. Есть идеи, как заставить что-то подобное работать, если в дереве 1 миллион узлов?

Спасибо, Дерек


Вот реализация подсчета с использованием CPS. Тем не менее, он все равно взорвал стек.

let count h =
    let rec count' h acc cont =
        match h with
        | E -> cont (1 + acc)
        | T(_,_,left,right) ->
            let f = (fun lc -> count' right lc cont)
            count' left acc f

    count' h 0 (fun (x: int) -> x)

Может быть, я смогу придумать способ разделить дерево на достаточно частей, которые я смогу сосчитать, не взрывая стек?

Кто-то спросил о коде, который генерирует дерево. Он ниже.

member this.ParallelHeaps threads =
    let rand = new Random()
    let maxVal = 1000000

    let rec heaper i h =
        if i < 1 then
            h
        else
            let heap = LeftistHeap.insert (rand.Next(100,2 * maxVal)) h
            heaper (i - 1) heap

    let heaps = Array.create threads E
    printfn "Creating heap of %d elements, with %d threads" maxVal threads
    let startTime = DateTime.Now
    seq { for i in 0 .. (threads - 1) ->
          async { Array.set heaps i (heaper (maxVal / threads) E) }}
    |> Async.Parallel
    |> Async.RunSynchronously 
    |> ignore

    printfn "Creating %d sub-heaps took %f milliseconds" threads (DateTime.Now - startTime).TotalMilliseconds
    let startTime = DateTime.Now

    Array.length heaps |> should_ equal threads <| "The size of the heaps array should match the number of threads to process the heaps"

    let rec reMerge i h =
        match i with 
        | -1 -> h
        | _  -> 
            printfn "heap[%d].count = %d" i (LeftistHeap.count heaps.[i])
            LeftistHeap.merge heaps.[i] (reMerge (i-1) h)

    let heap = reMerge (threads-1) E
    printfn "Merging %d heaps took %f milliseconds" threads (DateTime.Now - startTime).TotalMilliseconds
    printfn "heap min: %d" (LeftistHeap.findMin heap)

    LeftistHeap.count heap |> should_ equal maxVal <| "The count of the reMerged heap should equal maxVal"
11
задан Guy Coder 4 March 2016 в 15:16
поделиться