鸽子的装置 (2009)
Pigeon's Device (2009)

原始链接: http://pigeonsnest.co.uk/stuff/pigeons-device.html

鸽子设备是一种C语言循环优化技术,独立开发于Duff’s Device被广泛认知之前,目标相似,都是为了压缩条件逻辑。它源于比较具有不同排序顺序(正序、倒序以及“倒序日期,正序时间”REVDFWDT)的日期/时间记录的代码。 其核心思想是在`if`语句的`case`中嵌套一个`switch`语句。这允许函数根据“模式”变量有条件地执行不同的比较逻辑。如果模式为0,函数会根据与输入数据相关的一个条件来选择使用哪个比较函数。 最初的实现巧妙地通过使用“可怕的技巧”来间接设置排序模式,从而绕过了库排序函数的限制。REVDFWDT模式特别展示了该技术的强大之处,它以倒序的日期顺序进行排序,同时在每一天内保持时间的正序。本质上,鸽子设备提供了一种在单个函数中处理多个条件路径的简洁方法。

黑客新闻 新 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 鸽子的设备 (2009) (pigeonsnest.co.uk) 11 分,gaul 发表于 2 小时前 | 隐藏 | 过去 | 收藏 | 讨论 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请YC | 联系 搜索:
相关文章

原文
Pigeon's Device

Many may have heard tell of Duff's device, a rather neat loop optimisation technique in C.

This page provides details of Pigeon's device - a related but independently-originated technique. The original instantiation of Pigeon's device was in a piece of C code written for MS-DOS before the days of the internet, so I had not heard of Duff's device. When I did, the similarity between the techniques was immediately apparent.

The original Pigeon's device was in a function for comparing two date/time records in various different manners according to the desired sort order - FORWARD, forward chronological order; REVERSE, reverse chrononological order; and REVDFWDT, dates in reverse but times within each day sorted forwards. It's kind of a mess to read in its original form because of the various typedefs which are unique to the original program and other such cack, but the basic structure is quite simple. Here it is expressed as a function:

int pigeons_device(int a, int b, int mode) { int result; /* Isn't C a wonderful language? */ switch (mode) { case 0: if (gloop(a, b)) { case 1: result = arfle(a, b); break; } else { case 2: result = barfle(a, b); break; } } return (result); }

Or in other words... If mode == 1, return some function of a and b. If mode == 2, return a different function of a and b. But if mode == 0, decide which function of a and b to return based on some other relation between a and b.

Here it is with the possible flows of control marked out with pretty coloured arrows:

Control flows in Pigeon's device

Why might one want to do that? The simple answer is "because one is a pigeon". The more complicated answer is... well, here is the original implementation, from which you may work it out for yourself.

int lfdcmp(const void *a, const void *b) { static int mode; struct date d1, d2; struct time t; if (b==NULL) return(mode); if (a==NULL) { mode=*(int *)b; return(0); } /* Isn't C a wonderful language? */ switch (mode) { case REVDFWDT: unixtodos(((lfdy *)a)->stamplog, &d1, &t); unixtodos(((lfdy *)b)->stamplog, &d2, &t); if ((d1.da_day==d2.da_day) && (d1.da_mon==d2.da_mon) \ && (d1.da_year==d2.da_year)) { case FORWARD : return(longsub(((lfdy *)a)->stamplog,((lfdy *)b)->stamplog)); break; } else { case REVERSE : return(longsub(((lfdy *)b)->stamplog,((lfdy *)a)->stamplog)); break; } default : pigeonerror(FATAL,"Bad lfdcmp mode: %d\n\r",mode); break; } return(0); }

The function starts with a Gruesome Hack to set and read the comparison mode. I did this because the function is called from a library sort routine and there is no way to get that routine to pass a third "mode" parameter. There are various other ways to get the mode into the function but since it was intended in any case to specify that one should not set the mode directly but should instead use the functions setsortmode() and getsortmode(), I made the hidden workings inside those functions do it this way because it was more fun.

Then we move on to the Pigeon's Device proper. In the case of FORWARD sorting the function simply returns a value whose sign depends on the forward order of two date records - longsub() is a function which subtracts one value of type long from another and returns the result as a value of type int, but limited to the maximum and minimum values allowed for an int instead of overflowing; such a value will satisfy the requirements of the library sort function without further processing. In the case of REVERSE sorting the two records are simply compared the other way round.

In the case of REVDFWDT sorting - Reverse Date Forward Time - the first operation is to extract the date information from the records, which are in UNIX timestamp format which obfuscates midnight. So the dates are converted to a DOS format with separate values for date and time. Then we reach the if statement intertwined with the switch statement. If the two records relate to the same date, they are compared in forward order; if they relate to different dates, they are compared in reverse order. The result of this is that the overall list comes out sorted with the dates backwards but the times within each day still forwards.

The default case is a "bug catcher"; if the function is used as intended with the mode set by calling setsortmode() invalid values shouldn't get in there in the first place.

And that is Pigeon's device, in context.

Back to Pigeon's Nest

Be kind to pigeons

Valid HTML 4.01!

联系我们 contact @ memedata.com