Hacker News new | past | comments | ask | show | jobs | submit login
List Comprehensions in C (benlynn.blogspot.com)
63 points by fogus on April 8, 2011 | hide | past | favorite | 8 comments



Nice trick, but you end up selling a LOT of readability and maintainability just to get a "cool feature". When in Rome, act like the Romans: wouldn't a nested for loop do the trick just fine, in a way any C programmer can understand?


I do not think that author considers it useful in real world programming. It is a cool hack, therefore we should treat it like one, and not complain about readability.


I think it's more than a hack. List comprehension is a way to look at list manipulations which comes from the functional paradigm of programming. It is interesting and instructional to see how it can be expressed in a heavily imperative language like C. I would say it's a teaching tool, not just a hack.


Minor point: Extensive use of GNU extensions, so not really C.

Major point: Very much easier to do this in C++ by overloading operators for list<>:

    L2 = L1 + L3; // L2 is L1 concat L3
    L2 = L1 < 3; // L2 is L1 elements less than 3
    L2 = L1 | even; // L2 is L1 elements passed through a filter for even numbers


There is a C++ library proposal to implement (lazy) range algorithms and overload the operator| for their composition:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n187...

it would allow to do things like

  std::copy( ptr_vec | std::reversed | std::indirected,
             std::ostream_iterator<int>( std::cout ) );
Boost.Range implements part of the proposal, a much more complete implementation is Oven (http://p-stade.sourceforge.net/oven/doc/html/index.html), which includes the pipe operator, but I think it is unmaintained.

The result can be very readable and robust, I've seen complex production code using extensively this syntax.


Now Oven is fortunately maintained by awesome hackers in Japan. The latest C++ should compile against the trunk.

Regards,


That is not an accurate translation. Using the two lists is not the same as using one list, then the other; it does the Cartesian closure. You've got other problems, too, such as the fact that won't compose anything like a monad will, but that will do for a start.

Monads are trivial to implement in other languages if you skip the way they use functions and thereby implement not-actually-a-Monad. I won't say C++ can't do a monad nicely but it certainly isn't going to be that easy.


I implemented a rather nice C++ pattern matching facility for productions of context-free grammars in my undergraduate thesis project (http://www.grailplus.org). It is able to do some really fancy stuff and I think that is one were using C++ then one could use some similar techniques for creating list comprehensions. Here's how I envision it working:

First, the variables used in the comprehension would be defined locally, with the right types.

Second, we would need an overloaded template function, named something like "constructor" to abstractly represent a constructor for some arbitrary object. Here is how the constructor function works: for N arguments, all taken by reference, it allocates an array of N void * pointers (or sufficiently large slot sizes + proper alignment to hold any pointer type), and returns a pointer to the first slot in that array as some member in an instantiated template, where one of the parameters of the template contains all the info of the types of the constructor parameters. It also initializes each slot of the array to be the address of the local variable passed in to the constructor function by reference. So, if the following is my setup:

   int foo;
   float bar;
   constructor<std::pair>(foo, bar)
returns an object of type Constructor<std::pair, Slot<Base<int>,float> >, where the object contains a pointer to the first slot in the array. This type and the array contain all the info we need to construct an std::pair<int,float>.

The comprehensions would likely require an new iteration mechanism. This was the case in my thesis project, as a cursor-based iterator, while being able to represent the iteration aspect of this, is unable to fully represent the pattern-matching/destructuring bind operations. The tricky part is to make it so that one can advance multiple loops in sequence.

When we know that we can enter the body of the inner loop that would normally represent the innermost loop in an imperative implementation of the same operation being performed by the comprehension then we can use the pointer table to bind values to local variables, and then use a compile-time created function from the Constructor type to yank out those values all at once, impose the proper types using a cast on the void * pointers, and then construct the thing we want to add into our final list.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: