如何在函数内编写函数(list_map)

时间:2010-01-22 18:35:36

标签: c linked-list

你好,我最近问了一些关于C.的链表的问题 The link was found here

首先,我要感谢大家帮助我。但我有一个我无法理解的问题。我甚至问过教授,但他给我发了回信,但没有提供足够的信息。基本上我在C中写一个链表(见上面的链接)。教授在头文件中给我们的一点是:

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

所以我通过电子邮件向他发了电话并说:

  

另一个问题,在头文件中你没有定义一个排序函数,我们是否需要用原型编写一个排序函数,最后是什么是list_map

他回答说:

  

要求您实现一个排序函数f,它通过list_map(list,f)调用。希望它能清除你的疑虑。

我唯一怀疑的是这还没有完全教导。我可以理解如何对链表进行排序实际上这里有一些伪代码:

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;
}

我知道专家们可能会说这不是最有效的,我知道现在我只是在学习并试图让事情发挥作用。我可以清理后来......等等我的问题。

我的问题是我有排序功能(在教授的情况下,他称之为f)。当签名为:

时,我将如何调用此排序函数
void list_map(INTLIST* list, void (*f) (void*));

我会说:

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

或者我真的需要在某处定义list_map吗?我不是那种寻找某人工作的典型学生。我真的想尽力理解这一点。

感谢你们所有人。

[编辑部分]

我想补充说其中一张海报Kaleb P.说

  

“因此,您的工作是创建一个排序   你将传入的功能   list_map。请注意正确的语法   传递它将是:“

我的代码应该是这样的:

<。>在.h文件中我将函数原型化为:

void myCustomSort(void*);

然后在.cpp中它变为:

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; 
} 
}

要在主要方面调用它,我会这样做:

list_map(myListPointer, &myCustomSort); 

但是我不需要在任何地方定义list_map吗?因为它在.h文件中,我不必定义它吗?

5 个答案:

答案 0 :(得分:6)

假设list_map是这样实现的,按顺序给每个节点f

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

您可以实施selection sort

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

其中void swap_head_with_smallest(void *)将给定节点的数据与列表中跟随它的任何节点的最小数据交换。


由于这是家庭作业,我试图不给整个解决方案。

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 */
}

答案 1 :(得分:4)

你的教授正试图教你一个在函数式编程中常见的概念,即高阶函数的概念。高阶函数可以将其他函数作为参数,类似于

list_of_cosines = map(cos, list_of_inputs)

其中list of inputs是一系列浮点值,cos是正常的余弦函数。 map函数会为cos中的每个值调用list_of_inputs,并返回相应结果的列表。

C函数不能将其他函数类型作为参数,但它们可以将指针作为参数(通常称为回调);规范示例是qsort()库函数,它将一个指向函数的指针作为其参数之一,该函数接受两个指向void的指针并返回-1,0或1,具体取决于v1&lt; v2,v1 == v2,或v1&gt; v2,分别。例如:

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()会致电compareIntValues订购values中的元素。与数组表达式类似,函数指示符将根据上下文将其类型从“函数返回T”隐式转换为“指向函数返回T的指针”。

此时我猜测,但在我看来,您的教授希望您编写list_map函数,以便它将使用列表<调用排序函数f / em>作为参数,如下所示:

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
  ...
}

你教授提供的界面有点混乱;列表指针和f()的参数都应该是void *(在这种情况下你可以使用list_map将函数映射到不同的列表类型)或列表指针和f的参数都应该是INTLIST *(因为您似乎正在处理INTLIST类型)。

如果我是对的,那么表面上的exericise有点无意义(为什么不直接调用排序函数?),但可能是你的教授正在建立一些更通用的东西。毕竟,没有理由f必须是排序功能;它可以是显示列表,或将列表保存到文件或其他内容的功能。

我有一个不同的例子,说明如何使用回调对列表here进行排序;它可能有助于说明为什么这种方法很有用。

修改

eppient的list_map需要做的例子可能更接近你教授的意图而不是我写的内容。

答案 2 :(得分:1)

list_map的第二个参数是一个指向函数的指针,该函数返回void并引入一个void指针。由于list_map似乎是map函数,我猜它会为列表的每个元素调用f(指向函数的指针)。

因此,您的工作是创建一个您将传递给list_map的排序函数。请注意,传入它的正确语法是:

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

猜测传递给您的排序函数的void*可能是链接列表中的节点。

MergeSort是排序链接列表的不错选择。

答案 3 :(得分:0)

我相信list_map函数调用函数指针f(),它是一个指向一个带有void指针并返回void的函数的指针。如果是这种情况,这是实现排序但可行的疯狂方法。

定义类似

的功能
void Test(void *)
{...}

并将其传递给list_map(),如此

list_map(listptr,Test);

我会假设为列表中的每个项目调用Test函数。

答案 4 :(得分:0)

如果在您的节点中有一个指向列表头部的指针,则必须使用指向列表的指针作为前沿。让我解释一下。

map函数是函数式编程中的一个常见概念,现在,您只需要知道这是一个获取列表并且应用另一个函数(应用函数)到每个节点的函数。列表是给它的。我打赌你已经知道了。

到目前为止,我还记得C语言没有地图功能,所以你必须自己定义一个。这不是很困难:从列表的头部开始,然后走到尾巴。对于每个步骤,将当前listnode传递给执行您需要执行的操作的函数(在这种情况下是排序)。

现在有你的任务。

  • 您无法从应用函数中获取任何数据(它返回void)
  • 您必须走到列表末尾,将每个节点传递给执行排序的函数。
  • 对您尚未访问过的列表进行排序是没用的,因为您将继续为每个节点排序(排序已排序的集合,对我来说不太聪明);)
  • 您的应用函数获得单个指针。这清楚地表明指针(你所在的节点)代表一条线:在他的左边(到头部),列表的一部分已经排序,在右边(到尾部)有野生元素。 / LI>
  • 由于您的应用函数只是一个简单的void *输入,我认为最好不要指针并交换节点的有效负载

说,你的排序函数的伪代码,可能是其中一个,可以是:

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);
  }

}

或者,以更简洁的形式:

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

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