There are some things I am not sure of from a professional standpoint about the "correct" way to write C++ code. I have been looking through source code produced by various opensource projects, as well as other code posted here and on Stack Overflow.
So let's just leave it at this. Let's say I am interviewing for company A over the phone. Not white board yet. They ask me to write the code below and turn it in a hour. This is a hypothetical situation, however it parallels how most "big" companies are interviewing nowadays. Would this code "get me the job"? If not, what would you change, or what can you coach me with so I can get a job programming?
#include <iostream>
#include <queue>
#include <algorithm>
#include <iterator>
/*
* This program reads a sequence of pairs of nonnegative integers
* less than N from standard input (interpreting the pair p q to
* mean "connect object p to object q") and prints out pair
* representing objects that are not yet connected. It maintains an
* array id that has an entry for each object, with the property
* that id[p] and id[q] are equal if and only if p and q are
* connected.
*/
static const int N = 10;
int main( int argc, char *argv[ ] )
{
/*
* Ease the type of long types
*/
typedef std::ostream_iterator<int> output_data;
typedef typename std::vector<int>::iterator v_it;
/*
* Generate Test Data
*/
std::pair<int,int> pairs[12] = {
std::pair<int,int>( 3,4 ),
std::pair<int,int>( 4,9 ),
std::pair<int,int>( 8,0 ),
std::pair<int,int>( 2,3 ),
std::pair<int,int>( 5,6 ),
std::pair<int,int>( 2,9 ),
std::pair<int,int>( 5,9 ),
std::pair<int,int>( 7,3 ),
std::pair<int,int>( 4,8 ),
std::pair<int,int>( 5,6 ),
std::pair<int,int>( 0,2 ),
std::pair<int,int>( 6,1 )
};
/*
* Load Test Data onto Queue
*/
std::queue<std::pair<int,int> > queue;
for( int x=0;x<12;x++ )
{
queue.push( pairs[x] );
}
/*
* Data strucutre to represent nodes in graph.
* An index in the vector is an id for a node.
*/
std::vector<int> id;
/*
* Start the nodes as not being connected
*/
id.reserve( N );
for( int i = 0;i<N;i++ ) id.push_back( i );
std::cout << "p q\t";
/*
* Output the data
*/
copy( id.begin( ), id.end( ), output_data( std::cout, " " ) );
std::cout << std::endl;
std::cout << "------------------------------------";
std::cout << std::endl;
/*
* Algorithm to find out if wheter or not any two
* given pair p-q is connected. It does not show
* how they are connected.
*/
v_it start = id.begin( );
while( !queue.empty( ) )
{
std::pair<int,int> &pair = queue.front( );
int p = pair.first;
int q = pair.second;
int t = *(start+p);
if( t == *(start+q) )
{
}
else
{
for( v_it i = id.begin( ); i < id.end( ); i++ )
{
if( *(i) == t )
{
*(i) = *(start+q);
}
}
}
std::cout << p << " " << q << "\t";
copy( &id[0], &id[N], output_data( std::cout, " " ) );
std::cout << std::endl;
queue.pop( );
}
}
The output and arguments to gcc would look like this:
[mehoggan@desktop robert_sedgewick]$ g++ -o qf -Wall ./quick_find.cpp
[mehoggan@desktop robert_sedgewick]$ ./qf
p q 0 1 2 3 4 5 6 7 8 9
------------------------------------
3 4 0 1 2 4 4 5 6 7 8 9
4 9 0 1 2 9 9 5 6 7 8 9
8 0 0 1 2 9 9 5 6 7 0 9
2 3 0 1 9 9 9 5 6 7 0 9
5 6 0 1 9 9 9 6 6 7 0 9
2 9 0 1 9 9 9 6 6 7 0 9
5 9 0 1 9 9 9 9 9 7 0 9
7 3 0 1 9 9 9 9 9 9 0 9
4 8 0 1 0 0 0 0 0 0 0 0
5 6 0 1 0 0 0 0 0 0 0 0
0 2 0 1 0 0 0 0 0 0 0 0
6 1 1 1 1 1 1 1 1 1 1 1
Update
I made the changes but I have a few questions about semantics. I am going to ask those questions on Stack Overflow, so as to not pollute this post. Anyways here is the updated code (minus those filthy macros).
#include <iostream>
#include <queue>
#include <algorithm>
#include <iterator>
/*
* This program reads a sequence of pairs of nonnegative integers
* less than N from standard input (interpreting the pair p q to
* mean "connect object p to object q") and prints out pair
* representing objects that are not yet connected. It matains an
* array id that has an entry for each object, with the property
* that id[p] and id[q] are equal if and only if p and q are
* connected.
*/
static const int N = 10;
/*
* Ease the type of long types
*/
typedef std::ostream_iterator<int> output_data;
typedef std::vector<int> graph;
typedef graph::iterator v_it;
template <class T>
void display( T p, T q, const graph &id )
{
std::cout << p << " " << q << "\t";
std::copy( id.begin( ), id.end( ), output_data( std::cout, " " ) );
std::cout << '\n';
}
template <class T>
class sequence : public std::iterator<std::forward_iterator_tag, T>
{
private:
T val;
public:
sequence(T init) : val(init) { }
T operator *( ) { return val; }
sequence &operator++( ) { ++val; return *this; }
bool operator != ( const sequence &other ) { return val != other.val; }
};
template <class T>
sequence<T> gen_seq( const T &val )
{
return sequence<T>( val );
}
class process_node_quick_find
{
private:
graph &id;
public:
process_node_quick_find( graph &id ) : id( id ) { }
void operator( )( const std::pair<int,int> &pair )
{
int p = pair.first;
int q = pair.second;
int o = id[p];
int n = id[q];
/* Union Portion of Algorithm */
if( o != n )
{
std::replace( id.begin( ), id.end( ), o, n );
}
display( p, q, id );
}
};
class process_node_quick_union
{
private:
graph &id;
public:
process_node_quick_union( graph &id ) : id( id ) { }
void operator( )( const std::pair<int,int> &pair )
{
int p = pair.first;
int q = pair.second;
int i,j = 0;
/* Find */
for( i = p; i != id[i]; i = id[i] );
for( j = q; j != id[j]; j = id[j] );
/* Union */
if( i != j )
{
id[i] = j;
}
display( p, q, id );
}
};
int main( int argc, char *argv[ ] )
{
std::pair<int,int> data[ ] = {
std::make_pair( 3,4 ),
std::make_pair( 4,9 ),
std::make_pair( 8,0 ),
std::make_pair( 2,3 ),
std::make_pair( 5,6 ),
std::make_pair( 2,9 ),
std::make_pair( 5,9 ),
std::make_pair( 7,3 ),
std::make_pair( 4,8 ),
std::make_pair( 5,6 ),
std::make_pair( 0,2 ),
std::make_pair( 6,1 )
};
int M = sizeof(data)/sizeof(int)/2.0;
std::cout << "Going to insert " << M << " nodes" << std::endl;
graph id1( gen_seq(0), gen_seq( N ) );
display( 'p', 'q', id1 );
std::cout << std::string( N*3, '-' ) << std::endl;
std::for_each( &data[0], &data[M], process_node_quick_find( id1 ) );
std::cout << std::endl;
graph id2( gen_seq(0), gen_seq( N ) );
display( 'p', 'q', id2 );
std::cout << std::string( N*3, '-' ) << std::endl;
std::for_each( &data[0], &data[M], process_node_quick_union( id2 ) );
}