a poor man's linked-list sorting routine:

void
llist_sort(llist* ll)
{
    lnode* ln = ll->head;
    lnode* n = 0;
    lnode* p = 0;
    lnode* next = 0;
    lnode* prev = 0;

    while(ln) {
        next = n = ln->next;
        if (n) {
            while(ll->cb_cmp(ln->data, n->data) > 0) {
                n = n->next;
                if (!n)
                    break;
            }
            if (!n) {
                llist_unlink(ll, ln);
                ll->lnode_count++;
                ln->prev = ll->tail;
                ln->next = 0;
                ll->tail->next = ln;
                ll->tail = ln;
            }
            else if (n != next) {
                llist_unlink(ll, ln);
                ll->lnode_count++;
                n->prev->next = ln;
                ln->prev = n->prev;
                n->prev = ln;
                ln->next = n;
            }
        }
        prev = p = ln->prev;
        if (p) {
            while(ll->cb_cmp(ln->data, p->data) < 0) {
                p = p->prev;
                if (!p)
                    break;
            }
            if (!p) {
                llist_unlink(ll, ln);
                ll->lnode_count++;
                ll->head->prev = ln;
                ln->next = ll->head;
                ln->prev = 0;
                ll->head = ln;
            }
            else if (p != prev) {
                llist_unlink(ll, ln);
                ll->lnode_count++;
                ln->prev = p;
                p->next->prev = ln;
                ln->next = p->next;
                p->next = ln;
            }
        }
        ln = next;
    }
}

temporary labourer's statement:

i work in factories and wharehouses, packing, working like a machine, tied
to a machine, performing simple repetitious tasks.

working at a machine has something in common with programming - at a
stretch. i must be efficient in my movements, i must minimise my movements
to only those necessary to perform the job productively and simply. when i
am struggling to keep up with a machine, i must find those ways of doing
the task which mean less movement. i must tune my mind to not fussing on
details. when packing punnets into cardboard boxes, a) i have to tape a
flat-box into a box shape, b) i must place within the box a polythene
liner bag. usually boxes are made while waiting for the machine's next
cycle - but if you are not fast and efficient at making boxes, if you
worry about perfection in your work, making sure the box is properly
square and the seams are seamless, then fast you may not be and then you
get behind the machine cycle and have to catch up with it and the don't
have enough time to make boxes. likewise with placing the polythene liner
bag within the box, a balance has to be struck with throwing it in any-old
how, and placing it carefully so the stacks of punnets slide in without
resistance. if they get stuck on the bag the stack may fall apart and then
you have to spend time re-stacking the punnets in order to get all that
you must get into the box.

with coding, i've forgotten what the likeness was. efficiency. not of
physical movement of typing though that may help. but efficiency of code.
you can overly complexify your code. say you have to perform some specific
function which consists of various sub functions, it may make sense to
split those sub functions into various other functions rather than having
the whole lot as one big function. and, maybe, you can create those
sub-functions in some way which allows other functions to utilize them
also.

not only is the above code fragment for sorting a linked list a poor man's
in terms of the financial situation of the person who wrote the code (yes
me) but also in terms of the efficiency of the code. in the usage case the
code will be put to, this may not matter, but as a list for storing huge
amounts of data ( beyond mega-byte range ) it will prove to be poor indeed
(slow). but sorting routines are not my strongpoint and that was the first
thing i thought of how to do it without doing x and y or z - without
creating a new linked-list and sorting from one to the other for example.

a linked list is a data structure which holds a list of items (in this
case all items are of the same data type) each item links to the item
before it, and the item after it. this is opposed to an array. an array is
more like a box in which items are placed - once the box is full, it is
full (maybe you could tape another box onto it, maybe you could realloc
the array). a linked list is unlimited within reason. in your box you
place a stack of punnets. you find you need to remove one of the damaged
punnets. you must remove all punnets on top of it, take out the damaged
punnet, and replace all the others. a linked list on the other hand, you
just unlink the damaged punnet by setting the two links immediately either
side of it to link to each other bypassing the damaged punnet.

here is a nice pleasing looking function:

inline void*
llist_select_to_array(  llist* ll,
                        datacb_sel sel_cb,
                        const void* crit,
                        const void* terminator)
{
    lnode* first = llist_pri_select_flags(  ll,
                                            LNF_ASELECTED,
                                            sel_cb,
                                            crit    );

    return llist_pri_to_array(  ll,
                                ll->asel_count,
                                first,
                                terminator );
}

it converts a linked list into a box of punnets. first of all, it calls
the linked list's private function (which cannot be accessed outside of
the linkedlist implementation (ie in any code outside of llist.c) to
select items in the list. the selection criteria are unknown to the linked
list, all the list knows is a pointer to a memory location - it does not
even have any idea about what the contents of that memory location are -
all it "knows" (is told) is use call the function that is pointed to by
sel_cb for each item in the list and pass to that function the pointer to
the memory location where some kind of criteria exists that the selection
call back function will understand in god only knows what way.

so it does all that and is given as a reward the first item in the list
which meets the criteria - known by the secret codeword 'first'.
LNF_ASELECTED is the flag to use for selection - (when the GUI is finally
implemented in the distant future) normally LNF_SELECTED flag is used for
selection - this allows two different selections to exist simultaneously -
a public one which way down the line may converge with the user experience
- and a private one which the program keeps to it's self with dirty
pleasure.

so it does all that and it has a selection within the list to convert to
an array - maybe not the whole list, just a part, or maybe the whole list,
it does not matter. so it gives some info to another private function of
llist, llist_pri_to_array which converts the 'private' selection within
the list into an array.

inline void*
llist_to_array(const llist* ll, const void* terminator)
{
    return llist_pri_to_array(  ll,
                                ll->lnode_count,
                                ll->head,
                                terminator );
}

calls it also, but does not bother with any selection process.

static void*
llist_pri_to_array( const llist* ll,
                    size_t count,
                    const lnode* first,
                    const void* terminator)
{
    if (terminator)
        ++count;

    if (!count)
        return 0;

    void* arr = malloc(ll->data_size * count);

    if (!arr)
    {
        WARNING("out of memory copying llist to array\n");
        return 0;
    }

    void* i = arr;
    void* iend = (void*)((char*)i + ll->data_size *
                         (count - (size_t)(terminator ? 1 : 0)));

    const lnode* ln = first;

    while(ln && i <= iend)
    {
        if (ll->cb_copy)
            ll->cb_copy(i, ln->data);
        else
            memcpy(i, ln->data, ll->data_size);

        i = (void*)((char*)i + ll->data_size);
        ln = ln->next;
    }

    if (terminator)
    {
        if (ll->cb_copy)
            ll->cb_copy(i, terminator);
        else
            memcpy(i, terminator, ll->data_size);
    }

    return arr;
}

you may notice an error in this code. the author assumed that the
selection within the list would be contiguous - an assumption which should
be assumed to be faulty. instead of simply copying everything in the list
within the selection up to count itmes, it should check that the item is
actually selected or not.... but hang on one minute... ok ok, that's not a
problem... here's what the while loop should actually look like:

    while(ln && i <= iend)
    {
        if (ll->cb_copy)
            ll->cb_copy(i, ln->data);
        else
            memcpy(i, ln->data, ll->data_size);

        i = (void*)((char*)i + ll->data_size);
        ln = ln->next;
    }

ok, ok, that's exactly the same. i've not changed a thing, this is because
of an implementation problem caused by a lack of foresight on the part of
the author. yes, you're right to recall that there are two possible
selections. how is the llist_pri_to_array function to know which selection
to use when it is not told which selection to use?

ok, change #1

static void*
llist_pri_to_array( const llist* ll,
                    size_t count,
                    const lnode* first,
                    lnflags lnf,  /* <<<---- this! we added this! */
                    const void* terminator)

oh, but then the array might not be formed from a selection but from the
entire list!?!?

but just pass 0 as lnf in that case as hinty tat in enumeration:

typedef enum LNFLAGS
{
    LNF_SELECTED =  0x0001, /* public selection */
    LNF_ASELECTED = 0x0002  /* private selection */

} lnflags;


Sooooooooooo:

    while(ln && i <= iend)
    {
        if (!lnf || (ln->flags & lnf))
        {
            if (ll->cb_copy)
                ll->cb_copy(i, ln->data);
            else
                memcpy(i, ln->data, ll->data_size);

            i = (void*)((char*)i + ll->data_size);
        }
        ln = ln->next;
    }

the additional conditional test a) will copy an list item to the array if
no selection is made (ie lnf is zero) - OR - if lnf is LNF_SELECTED or
LNF_ASELECTED and the flags set in a particular list item match.

what the hell does "i = (void*)((char*)i + ll->data_size);" do? I here you
ask? well, recall the list does not know anything other than addresses of
memory locations where data resides - yes? good. therefor, to point to
those addresses there is only one option, a void pointer - this type of
pointer is very special, you can do sod all with it. next to useless to
your C newbie. but it can be used to point to a memory location of *any*
data type. the variable i, is a pointer to the array we're creating of
unknown data types - all we know is their size in bytes - stored in
ll->data_size (because the function to create a linked list insists upon
being told this kind of thing). pointer arithmetic on void pointers is not
quite legal C, so we cast the void pointer to a char pointer and a char is
a single byte (unless you're on some arcane bastard machine platform
specially designed to fuck up everything). yeah, so we 'cast' the void
pointer out to sea temporarily, turn it into a right character in the
process, add some number to it and store the result as the new value
(memory address) of the void pointer - ie the next element in this super
special array.

ooh mrs, what a palava. but a lovely palava i just can't help enjoying.
yes, it kept me occupied while i put-off writing the next part of my
marvellous boxyseq program. the boxy sequencer. pretty coloure boxes
appear in the screens simultaneous with midi messages sent to your
favourite (soft) synth to playu plinkyplankyplonky music.

see also: http://en.wikipedia.org/wiki/Big_O_notation

but i'm too thick to understand that shit.

_______________________________________________
NetBehaviour mailing list
NetBehaviour@netbehaviour.org
http://www.netbehaviour.org/mailman/listinfo/netbehaviour

Reply via email to