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

您好我最近在C中的链表上问过一些问题。
链接在这里找到

首先,我要感谢大家帮助我。 但我有一个我无法理解的问题。 我甚至问教授,但他给我回电子邮件的信息很少。 基本上我在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 data) { int x = tmp2->data; tmp2->data = tmp->data; tmp2->data = x; } tmp2=tmp2->next; } tmp=tmp->next; } 

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

我的问题是我有排序function(在教授的情况下,他称之为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 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文件中,我不必定义它吗?

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

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

您可以实现选择排序

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

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

 list_of_cosines = map(cos, list_of_inputs) 

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

C函数不能将其他函数类型作为参数,但它们可以将函数指针作为参数(通常称为回调 ); 规范示例是qsort()库函数,它将一个指向函数的指针作为其参数之一,该函数接受两个指向void的指针并返回-1,0或1,具体取决于v1 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 ,如下所示:

 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必须是一个排序function; 它可以是显示列表,或将列表保存到文件或其他内容的函数。

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

编辑

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

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

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

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

猜想传递给你的排序函数的void*可能是链表中的一个节点。

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

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

定义一个类似的函数

 void Test(void *) {...} 

并将其传递给list_map(),就像这样

 list_map(listptr,Test); 

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

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

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

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

现在有你的任务。

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

}

或者,以更简洁的forms:

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