How should I sort a doubly linked list the D way?

Johannes Loher johannes.loher at fg4f.de
Tue Aug 13 18:16:31 UTC 2019


Am 13.08.19 um 11:48 schrieb Mirjam Akkersdijk:
> Hello there,
> If I had a DLL, how would I sort it judging by the node contents, the D
> way?
> 
> In C if I were to sort a piece of malloc'd memory pointing to node
> pointers, I would write my compare function and let qsort sort it out.
> In D, I tried to use std.algorithm's sort functionality to no avail,
> because my guess would be it only operates on arrays. This opens up
> another can of worms as I am not fond of VLAs and D desperately wants to
> know what the size is at compile time.
> 
> Let's say we this trivial implementation:
> 
> Node* get_node(DLL* list, uint index);
> 
> struct Node {
>   int x;
>   Node* prev, next;
> };
> 
> struct DLL {
>   Node* head;
>   uint size;
> };
> 
> Node** nodes = cast(Node**) malloc(DLL.size * (Node*).sizeof);
> 
> and I would like to sort based on Node.t, how should I tackle it,
> preferably without resorting to C libraries?

I'd do something along the lines of this:

```
import std;

struct Node(T)
{
private:
    typeof(this)* prev, next;
public:
    T data;
}

struct DoublyLinkedList(T)
{
private:
    Node!T* head = null;
    Node!T* tail = null;

public:
    void pushFront(T t)
    {
        auto n = new Node!T;
        if (head != null)
        {
            head.prev = n;
        }
        else
        {
            tail = n;
        }
        n.data = t;
        n.next = head;
        head = n;
    }

    T front() const @property
    {
        return head.data;
    }

    void popFront()
    {
        head = head.next;
        if (head != null)
        {
            head.prev = null;
        }
        else
        {
            tail = null;
        }
    }

    void pushBack(T t)
    {
        auto n = new Node!T;
        if (tail != null)
        {
            tail.next = n;
        }
        else
        {
            head = n;
        }
        n.data = t;
        n.prev = tail;
        tail = n;
    }

    T back() const @property
    {
        return tail.data;
    }

    void popBack()
    {
        tail = tail.prev;
        if (tail != null)
        {
            tail.next = null;
        }
        else
        {
            head = null;
        }
    }

    size_t empty() const @property
    {
        return head == null;
    }

    auto nodes() @property
    {
        static struct NodePointerRange
        {
        private:
            Node!T* head;

        public:
            Node!T* front() @property
            {
                return head;
            }

            void popFront()
            {
                head = head.next;
            }

            bool empty() const @property
            {
                return head == null;
            }
        }

        return NodePointerRange(head);
    }
}

auto doublyLinkedList(R)(R r) if (isInputRange!R)
{
    DoublyLinkedList!(ElementType!R) list;

    foreach (e; r)
    {
        list.pushBack(e);
    }
    return list;
}

auto doublyLinkedListFromNodePointerRange(R)(R r)
        if (isInputRange!R && isPointer!(ElementType!R)
            && isInstanceOf!(Node, PointerTarget!(ElementType!R)))
{
    DoublyLinkedList!(TemplateArgsOf!(PointerTarget!(ElementType!R))) list;
    foreach (n; r)
    {
        n.prev = list.tail;
        if (list.tail != null)
        {
            list.tail.next = n;
        }
        else
        {
            list.head = n;
        }
        list.tail = n;
    }
    list.tail.next = null;
    return list;
}

void main()
{
    auto list = doublyLinkedList([5, 3, 7, 24, 1]);
    // looks natural but allocates new nodes
    auto sortedList = list.array.sort.doublyLinkedList;
    sortedList.each!writeln;

    writeln;

    auto anotherList = doublyLinkedList([54, 23, 8]);
    // looks a bit uglier but reuses the already allocated nodes
    auto anotherSortedList = anotherList.nodes
        .array
        .sort!((n1, n2) => n1.data < n2.data)
        .doublyLinkedListFromNodePointerRange;
    anotherSortedList.each!writeln;
}
```

Modulo bugs of course ;)

This uses the GC. If you want to avoid it in favor of malloc (or
std.experimental.allocator), you need to add `free`s (and possibly
referece counting) accordingly.




More information about the Digitalmars-d-learn mailing list