TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Two Star Programming

121 点作者 rix0r超过 12 年前

17 条评论

muyuu超过 12 年前
I don't see how Linus' version is better or more readable. By adding an indirection you have the equivalent of a dummy node, both ways are textbook examples of singly linked list deletion.<p>Note that you save no memory by not naming the implicit (in this implementation) dummy node. A pointer to a pointer remains a pointer to a pointer whether you name the intermediate pointer or not. Personally I think it's more readable to name it. Also, I prefer curr != NULL to simply putting *curr in a condition context. The compiler is going to do exactly the same.<p>I don't favour Linus' version because it's not readable. Most people who haven't seen this before will need to take some time to check this code. Out of the three basic ways of doing this I'd place this one dead last.
评论 #5031247 未加载
stiff超过 12 年前
With one of my friends at the university we used to compete for a shorter/nicer implementation of each data structure in the algorithms class we were taking, and after using pointers-to-pointers in some previous cases (linked lists, binary search trees etc.) and each time ending up with an implementation I really liked, I tried to implement AVL trees[1] the same way, for example an AVL rotation would be done by a call like this:<p><pre><code> avl_tree_rotate(avl, &#38;t-&#62;left-&#62;right-&#62;left, &#38;t-&#62;left-&#62;right, &#38;t-&#62;left, t-&#62;left-&#62;right); </code></pre> Where avl_tree_rotate looked like this (avl_tree_update just recomputes the height after left or right branch height changes):<p><pre><code> static void avl_tree_rotate(avl avl, V **from, V **to, V **parent, V *child) { if(*from) (*from)-&#62;parent = *to ? (*to)-&#62;parent : NULL; if(*to) (*to)-&#62;parent = *from ? (*from)-&#62;parent : NULL; avl_tree_swap(avl, from, to); child-&#62;parent = (*parent)-&#62;parent; if(!((*parent)-&#62;parent)) avl-&#62;root = child; else if((*parent)-&#62;parent-&#62;left == *parent) (*parent)-&#62;parent-&#62;left = child; else if((*parent)-&#62;parent-&#62;right == *parent) (*parent)-&#62;parent-&#62;right = child; (*parent)-&#62;parent = child; *from = *parent; avl_tree_update(avl, *parent); avl_tree_update(avl, child); } </code></pre> It was one of nicer programming workouts I ever got (the whole class was a great experience and I think every professional programmer should go through implementing all the basic data structures and algorithms in plain C one time), but I never managed to get deletion right, despite spending quite some hours on it, it just went beyond my head. Not that it matters for linked lists, and in this case I made it deliberately harder on myself, but I think the moral of the story is that when you are too clever with implementation details, you might run out of your cleverness when starting to deal with the actual problem domain.<p>[1] <a href="http://en.wikipedia.org/wiki/AVL_tree" rel="nofollow">http://en.wikipedia.org/wiki/AVL_tree</a>
评论 #5031466 未加载
评论 #5031453 未加载
kabdib超过 12 年前
The "two star" solution looks like it generates more memory traffic in the case of many deletions, and may run afoul of load-hit-store performance problems on some platforms, since the write to '*curr' in the remove case may still be in flight on its subsequent read and cause a pipeline stall.<p>Then again, the 'rm' predicate may dominate, or removes may be infrequent, and then who cares? Or your CPU may not care about LHS stalls (by virtue of being very whizzy, or just so terrible that the optimization doesn't matter).<p>Which is to say, measure and be prepared to be flexible rather than statically clever.
评论 #5031069 未加载
评论 #5031215 未加载
chrisbennet超过 12 年前
Unless I <i>really</i> need the performance, I avoid being "clever" when coding. The simple non-2 star example is going to be a lot faster for someone to understand, maintain and debug in the future.<p>"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." --Brian Kernighan
评论 #5031082 未加载
评论 #5031263 未加载
评论 #5031212 未加载
andrewcooke超过 12 年前
i had to write very similar code (deleting from a linked list) a couple of days ago. i wasn't the easiest code to write, and i needed several iterations before my tests were ok, but i remember feeling happy that the final result was clear and correct.<p>i just checked it and, yes, i used "two stars".<p>now i'm reading all the comments here, which are predominantly critical, calling this "clever" code. and it struck me just how lucky i am to work with decent programmers. where this kind of thing is considered quality work, not "showing off".<p>what i don't understand is why the other commenters here use pointers at all. couldn't you rewrite this to use arrays with integer indices? then you wouldn't need any stars (or very few), and it would be even "easier".<p>well, that's not true. i do understand. everyone thinks their own level is the correct one.<p>but still, i have sometimes been lucky enough to work with people who knew a lot more than i did. when i was in that position i didn't think they were "showing off". i thought they were awesome and tried to learn from them.
评论 #5031627 未加载
评论 #5032968 未加载
antirez超过 12 年前
The idea that you can classify a programmer's understanding of pointers based on doing linked list tricks is absurd. Usually Linus says things that make sense, but not this time IMHO.<p>Also the code proposed may work for linked lists that is a very well understood topic, but code like this in general is hard to understand and debug, without actually providing some <i>serious</i> advantage.<p>I remember some code on the Linux kernel implementing the inode caching in the virtual file system layer that was a mess like that, but multiplied by 1000. Very "Smart" indeed, but for the sake of what?
评论 #5031211 未加载
jules超过 12 年前
Is this really so amazing? I "invented" this long ago when I first implemented a binary tree. First you code the ugly version, and then in your mind or on paper you execute it and you see that there's nothing special about the root node, yet it is handled by a special case. Then you remove the special case, that's all. So keep in mind that contrary to what several people here are saying, it does not cause extra pointer dereferences.
loup-vaillant超过 12 年前
Something 's not right. Still too complex. Ah. Lists are recursive. Their treatment should be recursive as well. Let's see…<p><pre><code> void remove_if(node ** head, remove_fn rm) { node * entry = *head; if (rm(entry)) { *head = entry-&#62;next; free(entry); } else head = &#38;entry-&#62;next; remove_if(head, rm); } </code></pre> Ahh, much better: Now we can refactor:<p><pre><code> void remove_head(node ** head) { node * entry = *head; *head = entry-&#62;next; free(entry); } void remove_if(node ** head, remove_fn rm) { if (rm(*head)) remove_head(head) else head = &#38;entry-&#62;next; remove_if(head, rm); } </code></pre> I'm sure GCC will be able to optimize away the tail call.<p>Edit: oops, I forgot to test for empty lists, which was implicit in the for loop I set out to remove…
评论 #5031134 未加载
评论 #5031178 未加载
ambrop7超过 12 年前
Just for fun I made a benchmark for "two-star" and "one-star" versions of remove_if() and build() (to link an array of nodes). Code: <a href="http://ideone.com/D7bzmo" rel="nofollow">http://ideone.com/D7bzmo</a><p>Results with {gcc-4.7.2,clang-3.2} -march=corei7 -O3<p><pre><code> build_onestar+remove_if_onestar: 0m0.309s 0m0.357s build_onestar+remove_if_twostar: 0m0.302s 0m0.324s build_twostar+remove_if_onestar: 0m0.310s 0m0.361s build_twostar+remove_if_twostar: 0m0.302s 0m0.320s </code></pre> The two-star version of remove_if() is a bit faster here, but both build() versions are the same, across both compilers.
jiggy2011超过 12 年前
Not a C programmer , but if I was asked to implement this I would be more comfortable doing something like the first. The second took me a few moments to figure out.<p>Perhaps this is a result of many people learning in languages like Java where the idea of double indirection isn't quite so explicit so this style feels somewhat unnatural?<p>I could easily imagine introducing very weird bugs by mixing up my pointers and pointers-to-pointers. To mentally visualise a linked list I find it easier to just imagine a stack of boxes attached by string and work on the basis of cutting and reattaching the string to different points, this is more difficult under the 2nd approach.
huhtenberg超过 12 年前
The double-pointer trick makes for a more concise code, but it doesn't always lead to the code that is <i>faster</i>.<p>That said, it's actually an excellent C interview question. Combined with "write a binary tree iterator" it reliably measures one's exposure to thoughtful C programming.
评论 #5031136 未加载
评论 #5031058 未加载
csl超过 12 年前
Reminds me of the very funny description of a Three Star Programmer over at <a href="http://c2.com/cgi/wiki?ThreeStarProgrammer" rel="nofollow">http://c2.com/cgi/wiki?ThreeStarProgrammer</a><p>EDIT: I see it's also linked in the article.
twp超过 12 年前
A nice example of the old adage that "most problems in computer science can be solved by adding an extra layer of indirection".
评论 #5031507 未加载
bjoernbu超过 12 年前
Why is this supposed to be such a big deal? The second example uses the language's features to be able to use shorter code.<p>Usually there are countless possibilities for the exact same thing in every program. Personally, I would never say those who produce code with unused possibilities do "not understand" the corresponding language features.<p>Especially when writing example code or working with less experienced programmers, I even think that using less language features for the sake of making your algorithmic idea as obvious as possible is better practice.<p>For exmapel, I find it far more important that one decides wisely when to use a linked list at all or, if there are multiple fuctions for removal, chooses the right one for a particular use-case. Therefore everyone has to understand whats happening.
ruv超过 12 年前
Honestly as somebody who reads/ writes C every couple of months, I don't see what's so clever about it. This level of understanding for a C programmer should be the bare minimum, I think...
评论 #5035727 未加载
lsiebert超过 12 年前
You can just use a dummy node and track the previous entry. Then you don't have to ever pass back the front of the list, or treat the front of the list as special at all.<p>It's more elegant then the double level pointer, imho.
评论 #5031026 未加载
rjknight超过 12 年前
This makes me miss programming in C.