views:

290

answers:

5

Hello I recently asked some questions on linked lists in C.
The link was found here

First I want to thank everyone for helping me with this. But I have one issue I cannot understand. I even asked the professor but he emailed me back with little information. Basically I am writing a linked list in C (see above link). One of the things the professor gives us in the header file is this:

void list_map( INTLIST *list, void (*f)(void *) );
/*Applies a function to each element of the list */

So I emailed him about this and said:

Another question, in the header file you did not define a sorting function, do we need to write a sorting function with the prototype and finally what is list_map

And he replied with:

You are asked to implement a sorting function f, which is called through list_map(list, f). Hope it clears your doubts.

My only doubts are this was not fully taught. I can understand how to sort the linked list in fact here is some pseudo code:

tmp=head;

while(tmp!=NULL)
{
   tmp2=tmp->next; //pointer to next node
   while(tmp2!=NULL)
    {
     if (tmp2->data < tmp->data)
        {
         int x = tmp2->data;
         tmp2->data = tmp->data;
         tmp2->data = x;
        }
     tmp2=tmp2->next;
    }
   tmp=tmp->next;
}

I know the experts might say this is not the most efficient, and I understand that right now I am just learning and trying to get things working. I can clean up afterwords...so on to my question.

My question is given I have the sort function (in the professor's case he calls it f). How would I call this sorting function when the signature is:

void list_map(INTLIST* list, void (*f) (void*));

Would I just say:

list_map(myList, f()); //apply function f to the current linked list

Or do I really need to define list_map somewhere? I am not the typical student just looking for someone to do my work. I am really trying to understand this as best I can.

Thanks to all of you.

[EDIT PORTION]

I wanted to add that one of the posters Kaleb P. said

"Thus, your job is to create a sorting function that you will pass in to list_map. Note that the correct syntax for passing it in will be:"

So should my code simply be this:

in the .h file I prototype the function as:

void myCustomSort(void*);

And then in the .cpp it becomes:

void myCustomSort(void*f)
{
tmp=f->head; 

while(tmp!=NULL) 
{
   tmp2=tmp->next; //pointer to next node 
   while(tmp2!=NULL) 
   { 
     if (tmp2->data < tmp->data) 
        { 
         int x = tmp2->data; 
         tmp2->data = tmp->data; 
         tmp2->data = x; 
        } 
     tmp2=tmp2->next; 
    } 
   tmp=tmp->next; 
} 
}

And to call it in main I would just do:

list_map(myListPointer, &myCustomSort); 

But don't I need to define list_map anywhere? Because it is in the .h file do I not have to define it?

A: 

I believe that the list_map function calls the function pointer f() which is a pointer to a function that takes a void pointer and return void. If that is the case this is crazy way to implement a sort but doable.

Define a function like

void Test(void *)
{...}

And pass it in to the list_map() like so

list_map(listptr,Test);

I would assume your Test function gets called for each item in the list.

rerun
Ok but dont I need to define list_map anywhere or is that part of C?
You don't need the " just writing "Test" (with no parentheses) will result in the address of the function being passed to list_map.
Steve Emmerson
arg your right edited. If list_map is not defined you would need to define it.
rerun
+1  A: 

The second parameter to list_map is a pointer to function returning void and taking in a void pointer. Since list_map appears to be a map function, I would guess that it will call f (the pointer to a function) for each element of the list.

Thus, your job is to create a sorting function that you will pass in to list_map. Note that the correct syntax for passing it in will be:

void yourCustomSort(void*);
list_map(myList, yourCustomSort);

I would guess that the void* being passed into your sorting function will probably be to a node within the linked list.

MergeSort is a good choice for sorting linked lists.

Kaleb Pederson
So are you saying yourCustomSort would be my C code I posted in the post. and I would just say void yourCustomSort(void* f) { //my code to sort here } and to call this function in main all I would do is say list_map(myList, yourCustomSort); ???
Don't I need to define list_map anywhere or is this like delegates or something ?
I would assume `list_map` is provided, though it may not be. Although you provided some pseudo-code for sorting, it needs to take into account the `void*` that is being passed in and leverage it to accomplish the sorting.
Kaleb Pederson
@Kaleb - can you please check my edit in the original post, does it look right there ?
@jmh86 Ask your professor if you have to implement list_map() or if he's done that and will use it to test your sorting function.
Steve Emmerson
@jmh86 - Seems reasonable. In some cases it'll be better to swap the linked list nodes rather than moving the data between nodes.
Kaleb Pederson
@kaleb when you say seems reasonable are you stating that what I posted in my edit portion might just work? Am I calling it correctly?
@jmh86 - Yes, the edit portion looks like it might work. Note that you don't call `list_map(myList, f())` as you wrote. What you wrote would try to pass in the results of evaluating the function `f`.
Kaleb Pederson
@kaleb I am not understanding you in the last comment. Can you please state what / how I should be calling this ?
@jmh86 - Both my and @rerun's example demonstrate how to call it correctly.
Kaleb Pederson
+6  A: 

Assuming list_map is implemented like this, giving f each node in sequential order,

void list_map(INTLIST *list, void (*f)(void *)) {
    INTLIST *node;
    for (node = list; node; node = node->next)
        f(node);
}

you can implement a selection sort

void list_sort(INTLIST *list) {
    list_map(list, swap_head_with_smallest);
}

where void swap_head_with_smallest(void *) swaps the datum of a given node with the smallest of the data of any nodes following it in the list.


As this is homework, I'm trying not to give the whole solution away.

void swap_head_with_smallest(void *list) {
    INTLIST *head = list;
    INTLIST *smallest;

    /* set smallest the smallest node of
         head, head->tail, head->tail->tail, etc. */

    /* swap head->datum and smallest->datum */
}
ephemient
My question is do I need to implement list_map? He has it as a prototype in the .h file. His only comment was:You are asked to implement a sorting function f, which is called through list_map(list, f). Hope it clears your doubts. Which doesn't give me much information. I can understand your list_map function but I guess when you say f(node); do I need to define f or is it already defined...
This is pretty much the only answer that really works for this question. I'd hate to have a professor that asks such badly designed questions though - sorting is the last thing you'd want to use a map() for.
Max Shawabkeh
@Max I agree but I am not the professor. Very poor teaching...
D'oh; that's obviously what he meant. I'm going to edit my answer to point to this one.
John Bode
@Max: it's not a proper CS program without at least one confusing (or possibly confused) professor. Bonus points if he's the department chair.
John Bode
I am starting to understand this some more but I have a question when you said f(node) you are passing the node to a function (just one node in the linked list). How am i going to compare that node with the rest of the linked list to swap head values ?
John Bonde + 1 for that comment you aint kiddin...
Ok +1 for this this one seems to make the most sense. I am going to pass a pointer to that function (f) and use a selection sort. Go through each item and compare then swap datums. Then it will go back to that for loop, go to the next node, I pass that to f and compare that node with the existing nodes, swap datums...keep doing this till everytihng is sorted. +1 for the hint...
@jmh86: you can treat the node passed to `f` as the head of a sublist; from there, you can access the nodes after it via the `next` member. So building on ephemient's example, `swap_head_with_smallest` will start from the input node, then walk down all the nodes following it via the `next` member, and if a following node has a data value less than the current node's, swap them. Like Max says, this isn't a great use of a map function, but oh well.
John Bode
I agree I want to thank you both, John even though you might have been off I liked your explanation it has helped me understand function pointers..so I did learn something. I will probably accept this answer but you did help a lot..and I think I understand how to do the swapping as it looks like it is a pointer to the head and then i just traverse the list. Then the next time im in the for loop I then compare that node with the rest of the list.
I think this post along with John's helped me and hinted me in the right direction. I ended up defining the sort function, and I understand how mapping works now. I then defined the selection sort which although is pretty slow Big (On^2) I think it is suitable for this silly assignment!
+4  A: 

Your professor is trying to teach you a concept that's common in functional programming, which is the idea of a higher-order function. A higher-order function can take other functions as parameters, sort of like

list_of_cosines = map(cos, list_of_inputs)

where list of inputs is a series of floating point values, and cos is the normal cosine function. The map function will call cos for each value in list_of_inputs and return a list of the corresponding results.

C functions cannot take other function types as parameters, but they can take pointers to functions as parameters (usually termed a callback); the canonical example is the qsort() library function, which takes as one of its parameters a pointer to a function that accepts two pointers to void and returns -1, 0, or 1 depending on whether v1 < v2, v1 == v2, or v1 > v2, respectively. For example:

int compareIntValues(const void *v1, const void *v2)
{
  int lv1 = *(int *) v1; // convert inputs from pointers to void
  int lv2 = *(int *) v2; // to the type we're actually interested in
  if (lv1 < lv2) return -1;
  if (lv1 > lv2) return 1;
  return 0;
}

int main(void)
{
  int values[] = {3, 1, 4, 5, 7, 9, 6, 2};
  ...
  qsort(values,                             // buffer containing items to sort
        sizeof values / sizeof values[0],   // number of items to sort
        sizeof values[0],                   // size of each item
        compareIntValues);                  // comparison function
  ... 
}

qsort() will then call compareIntValues to order the elements in values. Similar to array expressions, a function designator will have its type implicitly converted from "function returning T" to "pointer to function returning T" depending on the context.

At this point I'm guessing, but it looks to me like your professor wants you to write the list_map function so that it will call a sorting function f with the list as the parameter, something like the following:

void list_map(INTLIST *list, void (*f)(void *))
{
  // sort the list by passing it to f
  f(list); // or (*f)(list);
}

void sortListAscending(void *ptr)
{
  INTLIST *ilptr = ptr;
  /**
   * sort the list in ascending order
   */
}

void sortListDescending(void *ptr)
{
  INTLIST *ilptr = ptr;
  /**
   * sort the list in descending order
   */
}

int main(void)
{
  INTLIST *theList;
  ...
  list_map(theList, sortListAscending); // sort the list in ascending order
  ...
  list_map(theList, sortListDescending); // sort the list in descending order
  ...
}

The interface provided by your professor is a bit confused; either both the list pointer and the parameter to f() should be void * (in which case you could use list_map to map functions to different list types) or both the list pointer and parameter to f should be INTLIST * (since you seem to be dealing with INTLIST types).

If I'm right, then the exericise is a bit pointless on the surface (why not call the sorting function directly?), but it could be your professor is building up to something a bit more general-purpose. After all, there's no reason that f has to be a sorting function; it could be a function to display the list, or to save the list to a file, or something else.

I have a different example of how to use callbacks to sort a list here; it may help illustrate why this method is useful.

EDIT

ephemient's example of what list_map needs to do is probably much closer to what your professor intends than what I wrote.

John Bode
Wow, this is exactly the explanation I need. I think I am understanding this a bit more based on your entire passage :). So what you are saying is I call list_map and I pass to it my list (which is really a pointer to a linked list) and the name of a function. Inside of list_map the first parameter is simply a pointer to the list..easy..the second part is simply a POINTER to that function defined but is now termed simply "f". Now I simply refer to the function as f rather then the name of the function (being that it is a pointer to a function). +1 I am goign to try this at home. THANKYOU
While this is a very good explanation, typically a map() doesn't work this way - it is applied to each element. This is confirmed by the comment in the function header in the OP: `/*Applies a function to each element of the list */`
Max Shawabkeh
Yeah, I've probably garbled what the professor was intending; I'm just guessing based on the interface provided, so I wouldn't be surprised if I misread the whole thing.
John Bode
Function programming in C is... awkward, at best. This is a good explication of an otherwise hairy concept, even if we had somewhat different interpretations on the professor's intent.
ephemient
A: 

If in your node there is a pointer to the head of the list you have to use the pointer to the list as a frontier. Let me explain.

The map function is a common concept in functional programming, for now, you have just to know that is a function that get a list and apply another function (the applied function) to every node of the list was given to it. I bet you already knew it.

C language haven't, as far I remember, a map function, so you have to define one by your own it. It is not very difficult: Just start from the head of the list and walk to the tail. For every step pass the current listnode to the function that perform the operation you need to be performed (in this case the sort).

Now there is your assignment.

  • You cant get any data from the applied function (it returns void)
  • You have to walk down until the end of the list passing every single node to the function that do the sorting.
  • It is useless to sort the list you haven't already visited, as you would keep to sort it for every node (sorting an already sorted set it is not too smart to me) ;)
  • Your applied function get a single pointer. This in clearly stating that that pointer (the node you are at) represent a line: on his left (to the head) there is the part of list already sorted, to the right (to the tail) there are the wild elements.
  • Since Your applied function get as input just a simple void * , I think it is better to leave the pointers alone and exchange the payload of the nodes

Said that, the pseudocode for your sorting function, one of the possibile ones, can be:

void ascendingSort(void*f)
{
  cursor=f->head; 
  placed=false;

  while(!placed and cursor!=f) { 
    if (cursor->data < f->data) {
      cursor = cursor->next;
    } else {
      swap( cursor->data, f->data);
      placed=true;
    }
  }

  while(cursor!=f) { 
      cursor = cursor->next;
      swap(cursor->data, f->data);
  }

}

Or, in a more concise form:

void ascendingSort(void*f)
{
  cursor=f->head; 

  while(cursor!=f) { 
    if (cursor->data > f->data) {
      swap( cursor->data, f->data);
    }
    cursor = cursor->next;
  }
}
Eineki