This is a simple (mutable) Deque implementation I've got lying around.

It allows insertion and deletion both at the front and the back with an

amortized O(1) complexity, and it also supports O(1) random access. The PushFront and PushBack methods return a 64-bit index you can later use to access the element directly. The index is a 64-bit integer in order to be on the safe side in case you PushBack and PopFront more than 2^31 elements.

If you want to paste this code into the fsi console, you need to delete the "inline" from the Inc/Dec member declaration, I don't know why.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// License: Same as FParsec, see http://www.quanttec.com/fparsec/#license

type Deque<'a>() =
    let mutable buffer = Array.zeroCreate 16 : 'a[]
    let mutable mask = 16 - 1
    let mutable first = 0
    let mutable next = 0
    let mutable count  = 0
    let mutable firstIndex = 0L


    member inline private t.IncFirst() = first <- (first + 1) &&& mask
    member inline private t.IncNext()  = next  <- (next  + 1) &&& mask
    member inline private t.DecFirst() = first <- (first + mask) &&& mask
    member inline private t.DecNext()  = next  <- (next +  mask) &&& mask


    member t.Count = count
    member t.FirstIndex = firstIndex


    member private t.Grow() =
        let n = buffer.Length
        let a = Array.zeroCreate (2*n)
        System.Array.Copy(buffer, first, a, 0, n - first)
        System.Array.Copy(buffer, 0, a, n - first, first)
        buffer <- a
        first <- 0
        next <- n
        mask <- 2*n - 1


    member t.PushFront(e) =
        t.DecFirst()
        buffer.[first] <- e
        let c = count + 1
        count <- c
        if c = buffer.Length then t.Grow()
        firstIndex <- firstIndex - 1L
        firstIndex


    member t.PushBack(e) =
        buffer.[next] <- e
        t.IncNext()
        let c = count + 1
        count <- c
        if c = buffer.Length then t.Grow()
        firstIndex + int64 (c - 1)


    member t.PopFront()  =
        if first <> next then
            let e = buffer.[first]
            t.IncFirst()
            count <- count - 1
            firstIndex <- firstIndex + 1L
            e
        else raise (System.InvalidOperationException("The deque is empty."))


    member t.PopBack() =
        if first <> next then
            t.DecNext()
            count <- count - 1
            buffer.[next]
        else raise (System.InvalidOperationException("The deque is empty."))


    member t.PeekFront()  =
        if first <> next then
            buffer.[first]
        else raise (System.InvalidOperationException("The deque is empty."))


    member t.PeekBack()  =
        if first <> next then
            buffer.[(next + mask) &&& mask]
        else raise (System.InvalidOperationException("The deque is empty."))


    member t.Item
        with get index   = let off = index - firstIndex
                           if off >= 0L && off < int64 count then
                               buffer.[(first + int32 off) &&& mask]
                           else raise (System.ArgumentOutOfRangeException("index"))
        and  set index v = let off = index - firstIndex
                           if off >= 0L && off < int64 count then
                               buffer.[(first + int32 off) &&& mask] <- v
                           else raise (System.ArgumentOutOfRangeException("index"))
By on 5/30/2009 2:02 AM ()

Does System.Collections.Generic.List<'T> fit your needs?

By on 5/28/2009 3:15 PM ()

Does System.Collections.Generic.List<'T> fit your needs?

Not quiet. Appending elements to this list is O(n). And I do need to grow it from the end. For now I took the standard F# list and build the list in the reversed order by prepending the items to the list. When I am done building the list I am reversing it with List.rev. I was thinking about using the

1
System.Collections.Generic.LinkedList

, but then I would loose the convineince of native F# lists. Using the F# list and then reversing it is slower but only by O(n)

By on 5/29/2009 2:51 PM ()

Filling in F# list + reversing is O(n). Filling in general doubly linked list is also O(n).

By on 5/30/2009 12:56 AM ()

Actually it's wrong. Appending to end is O(1), accessing nth is also O(1).

Inserting takes O(n) in general case. Do you need inserting?

By on 5/30/2009 12:55 AM ()

Actually it's wrong. Appending to end is O(1), accessing nth is also O(1). Inserting takes O(n) in general case. Do you need inserting?

Appending to the end of a single linked list involves traversing the list from the head to the end which is O(n). Repeated as many times as many items you have in the list (n) gives you O(n^2) for bulding the list by appending items to the end one by one.

Adding items to the beginning of the single linked list is O(1) times n items in the list gives you O(n) to build the list, The list though will be in reversed order, but reversing the order of a list already built is O(n) which is still O(n) for the entire process of list building.

This is what the "theory" is telling and it is confirmed by the results I am getting on my templates with and without modifications. Switching to the second way of building the list considerably sped up template parsing

By on 5/31/2009 7:59 AM ()

I meant System.Collections.Generic.List<T>.

By on 5/31/2009 10:03 AM ()

"Appending to the end of a single linked list involves traversing the

list from the head to the end which is O(n). Repeated as many times as

many items you have in the list (n) gives you O(n^2) for bulding the

list by appending items to the end one by one."

Why is it implemented this way? Using just O(1) more space one can keep a pointer to the end, so that both appending and prepending take O(1)

By on 5/31/2009 11:34 AM ()

Why is it implemented this way? Using just O(1) more space one can keep a pointer to the end, so that both appending and prepending take O(1)

F# lists are immutable. Allowing the end of the list to be mutable would make them... um, mutable. One nice thing about immutable lists is that the "tail" part can be safely shared amongst several references, even across different threads. This also holds true for F# immutabe sets and maps.

By on 5/31/2009 9:01 PM ()

I guess that depends a bit on which of the two ends of the list you define to be "the end" ;-)

By on 5/31/2009 8:14 AM ()

System.Collections.Generic.LinkedList<T> is a double linked list, but if you are just appending List<T> is more efficient.

By on 5/28/2009 8:46 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper