Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

Δημοσ.

Δεν έπρεπε να κλωτσήσεις τοτε τέτοια ευκαιρία :D

Φίλε μου δουλεύω πέντε χρόνια στον χώρο και μπορώ να σου πω ότι το να γράφεις τέστ χωρίς να είσαι σε μια σορτλιστ η έστω να εχεις περάσει μια συνεντευξη πρωτα ειναι χασιμο χρονου. Ειδικά αν εργάζεσαι και εχεις προϋπηρεσία. Τώρα αν είσαι φοιτητής και μολις εχεις βγει καλώς. Αλλά σκέψου κάποιον με εμπειρία σε καλή εταιρία χωρίς να το έχουν δει να ζητάνε να γράψει τέστ έτσι γιατί έτσι ειναι η διαδικασία .Στην τελικη πολυ απλά μπορεί να μην μου κάνουν εμένα. Γιατί να κάτσω να γράψω πρώτα για να μην γουστάρω εγώ μετά βάσει όσων θα μου πουν. Μαζί και ο μισθός .

 

Στα λέει αυτά άτομο που εταιρία που βγάζει πιστοποιήσεις τον εβαλε να γράψει πάλι τέστ στα ίδια πράγματα για να τον προσλάβει .

 

Μου αρέσει που σου απαντησα κιόλας στο topic με τους java constructors

  • Απαντ. 54
  • Δημ.
  • Τελ. απάντηση

Συχνή συμμετοχή στο θέμα

Συχνή συμμετοχή στο θέμα

Δημοσ.

Φίλε μου δουλεύω πέντε χρόνια στον χώρο και μπορώ να σου πω ότι το να γράφεις τέστ χωρίς να είσαι σε μια σορτλιστ η έστω να εχεις περάσει μια συνεντευξη πρωτα ειναι χασιμο χρονου. Ειδικά αν εργάζεσαι και εχεις προϋπηρεσία. Τώρα αν είσαι φοιτητής και μολις εχεις βγει καλώς. Αλλά σκέψου κάποιον με εμπειρία σε καλή εταιρία χωρίς να το έχουν δει να ζητάνε να γράψει τέστ έτσι γιατί έτσι ειναι η διαδικασία .Στην τελικη πολυ απλά μπορεί να μην μου κάνουν εμένα. Γιατί να κάτσω να γράψω πρώτα για να μην γουστάρω εγώ μετά βάσει όσων θα μου πουν. Μαζί και ο μισθός .

 

Στα λέει αυτά άτομο που εταιρία που βγάζει πιστοποιήσεις τον εβαλε να γράψει πάλι τέστ στα ίδια πράγματα για να τον προσλάβει .

 

Μου αρέσει που σου απαντησα κιόλας στο topic με τους java constructors

 

αχχαχαχχαχαχαχαχα κορυφαίος!

 

Ειρωνικά το είπα ρε φίλε μην αγριεύεις :D

Πλακα εκανα. Να πάρουμε λιγο γραμμη τον εργοδοτη ειπα και εγω

 

Μηπως εισαι κουρασμενος απο την πολλη προυπηρεσια και δεν το καταλαβες? :D

 

Το ειδα το σχολιο σου στους constructors και σε ευχαριστω !

Δημοσ.

Στη Σίλικον βάλει που ήταν τι να πεις... :)

 

Αμερικανάκια φίλε , διαόλου κάλτσες ! :D

Τελοςπαντων... σορρυ για το οφφ τοπικ συνάδελφοι.

Καλη συνεχεια σε ολους.

Δημοσ.

Λοιπόν η τακτική που ακολούθησα για να λύσω την άσκηση ήταν η απλούστερη καθώς δεν είχα και πολύ χρόνο διαθέσιμο και δεν προχώρησα σε κανένα optimization. Η προσέγγιση μου ήταν παρόμοια με αυτή που χρησιμοποιούν πολλοί σε networking code με non-blocking sockets και I/O multiplexing.Έχεις ένα buffer ο οποίος πρέπει να είναι τουλάχιστον τόσο μεγάλος όσο και η μεγαλύτερη "εντολή" που μπορείς να λάβεις από τον peer (αν και συνήθως είναι πολύ μεγαλύτεροι για λόγους αποδοτικότητας) και κάθε read(..., n) από socket file descriptor μπορεί να σου επιστρέψει από 1 μέχρι n bytes. Για τον λόγο αυτό με το που λάβεις κάποια bytes από το άλλο άκρο τα κοτσάρεις στο τέλος του buffer και ελέγχεις εάν ο buffer περιέχει ένα η περισσότερα valid commands. Για παράδειγμα εάν έχουμε 3 valid εντολές: START, STOP, SHUTDOWN τότε θα μπορούσαμε να έχουμε την εξής ακολουθία:

 

buffer = empty

 

peer sends: "START"

kernel receives: "ST"

read() returns: "ST"

buffer = "ST"

 

CHECK(buffer) for valid commands -> NONE

 

kernel receives: "ART"

read() returns: "ART"

buffer = "ST" + "ART" = "START"

 

CHECK(buffer) -> Execute command START

κτλ.

 

Με παρόμοιο τρόπο έλυσα την άσκηση. Φυσικά είναι inefficient διότι κάνει heavy use συναρτήσεων όπως memcpy, memmove, strlen, strchr κτλ. Κάποιες απο αυτές τις συναρτήσεις θα μπορούσαν να αποφευχθούν με εύκολο τρόπο (strlen -> με την χρήση επιπλέον μεταβλητής η με πράξεις) και άλλες με δυσκολότερο (memmove -> χρήση ring buffer αντί για απλό buffer). Επίσης ο έλεγχος για valid εντολές γίνεται με σειριακό τρόπο: Έλεγχος πρώτης εντολής, έλεγχος επόμενης, κτλ. χωρίς την χρήση lexer/grammar.

 

Θα ανεβάσω αύριο το μεσημέρι τον κώδικα να τον δείτε και να κράξετε :)

Δημοσ.

Παρακάτω φαίνεται ο κώδικας του parser που βρίσκεται στο αρχείο: main.cpp

 

Για να μπορέσω να δοκιμάσω την ανεκτικότητα του parser σε σφάλματα έχω προσθέσει το command line option -i το οποίο λέει στο πρόγραμμα να διαβάζει το input από STDIN και σε περίπτωση που βρει κάποια εντολή να εκτυπώνει το code της εντολής, τον αριθμό των παραμέτρων, καθώς και την τιμή κάθε παραμέτρου όπως φαίνεται παρακάτω:

 

$ ./ModeLightingParser -i

GARBAGE DATA

EMPTY

SCENE1GO

1 1 1

AHSDHBAS HABS DHBSAD HBJSCENE110T2345GOASDASD

2 2 110 2345

 

Αυτό το functionality (με το -i) είναι πολύ χρήσιμο για τον έλεγχο της ορθότητας του parser όπως εξηγώ παρακάτω. Χωρίς κανένα option το πρόγραμμα απλά τρέχει τα Checks που έχει καθορίσει η εταιρεία στην main().

 

>
/**
* A solution to the Mode Lighting exercise 
* (http://modelighting.com/photos/embedded-software-eng-job.html).
* 
* - Compiled with g++ 4.5.2 on linux.
* 
* NOTE:
* -----
* Instead of using multiple '%' characters to denote numbers in a command, 
* the program enables the user to specify number ranges in the form [x-y] or
* [x], such that instead of specifying multiple commands as shown below:
* SCENE%GO,,SCENE%%GO,SCENE%%%GO,SCENE%%%%GO
* a single command can be used: SCENE%[1-4]GO
*/

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <climits>

using namespace std;

/**
*  Command codes as returned by the parser when a given command is identified.
*/
typedef enum CommandSet {
csUnknown = 0,
csSceneGo = 1, // SCENE*GO
csSceneTGo = 2, // SCENE*T*GO
csSceneTLGo = 3, // SCENE*T*L*GO
csSceneNu = 4, // SCENE*NU
csSceneNd = 5, // SCENE*ND
csSceneUp = 6, // SCENE*UP
csSceneDn = 7, // SCENE*DN
csSceneSt = 8, // SCENE*ST
csSequenceGo = 9, // SEQUENCE*GO
csSequenceSt = 10, // SEQUENCE*ST
csSetSlaveChan = 11, // S*C*L*GO
csSetPackChan = 12, // P*C*L*GO
csSetPackDmx = 13, // P*D*L*GO
csSetModuleChan = 14, // M*D*C*L*GO
csSetButtonState = 15 // P*B*S*\r\n, P*B*S*GO

} CommandSet_t;

// --------------------------- Constants ---------------------------

// Maximum number of digits for a decimal plus a null.
static const int Max_digits = 5;
// Maximum number of arguments for the available commands.
static const int Max_args = 4;

// ---------------------- Function prototypes ----------------------

enum lentype {
LT_MIN, LT_MAX
};

static size_t get_cmd_len(const char *cmd, lentype);
static int get_range(const char *str, int *min, int *max);

// ---------------------- Function definitions ---------------------

/**
* Get the minimum/maximum number of characters for a specific command.
* 
* @param cmd const char *
* @return size_t
*/
static size_t get_cmd_len(const char *cmd, lentype t) {
size_t len = 0;
const char *p = cmd;

// Catch invalid calls early on.
if (cmd == NULL) {
   	cerr << "Warning: NULL pointer passed as a parameter to the "
           	"get_cmd_len function.\n";
   	return 0;
}

while (*p) {
   	if (*p == '%') {
       	int min, max;
       	get_range(++p, &min, &max);

       	if (t == LT_MIN)
           	len += min;
       	else if (t == LT_MAX)
           	len += max;

       	p = strchr(p, ']');

       	if (p == NULL) {
           	cerr << "Fatal error parsing command "
                   	"(Character ']' not found)\n";
           	exit(1);
       	}

       	p++;
       	continue;
   	}
   	p++;
   	len++;
}

return len;
}

// ------------------------------------------------------------------

/**
* Given a range in the form [x-y], where x and y are numbers, extract x and
* y values and return them to min and max memory locations, respectively.
* If an error occurs the functions returns 1, otherwise it returns 0.
* min, max or both parameters can be NULL.
* 
* @param str const char *
* @param min int *
* @param max int *
* @return int
*/
static int get_range(const char *str, int *min, int *max) {
char *p, *s, *orig;

// Catch invalid calls.
if (str == NULL || *str++ != '[')
   	return 1;

// Duplicate char array in order to be able to manipulate it.
p = s = orig = strdup(str);

if (orig == NULL) {
   	cerr << "Insufficient memory - terminating\n";
   	exit(1);
}

if ((p = strchr(s, '-')) == NULL) {
   	p = strrchr(s, ']');

   	if (!p) {
       	free(orig);
       	return 1;
   	}
   	*p = 0;

   	if (min)
       	*min = atoi(s);
   	if (max)
       	*max = atoi(s);
} else {
   	*p++ = 0;

   	if (min)
       	*min = atoi(s);

   	s = p;

   	p = strrchr(s, ']');

   	if (!p) {
       	free(orig);
       	return 1;
   	}
   	*p = 0;

   	if (max)
       	*max = atoi(s);
}

free(orig);
return 0;
}

// ---------------------- ParserResult Class ---------------------

/**
*  A single 'token found' result interface, providing ways of retrieving
*  the command code and given parameters if any.
*/
class IParserResult {
public:
virtual CommandSet_t getCommand() const = 0;
virtual int getParamCount() const = 0;
virtual int getParamAtIndex(const int index) const = 0;
};

class ParserResult : public IParserResult {
private:
vector<int> args;
CommandSet_t cmdcode;
public:
ParserResult(CommandSet_t code, int argp, char (*arguments)[Max_digits]);
CommandSet_t getCommand() const;
int getParamCount() const;
int getParamAtIndex(const int index) const;
};

ParserResult::ParserResult(CommandSet_t code, int argp,
   	char (*arguments)[Max_digits]) {
cmdcode = code;

// Add all the arguments that were found to a vector.
for (int i = 0; i < argp; i++)
   	args.push_back(atoi(arguments[i]));
}

CommandSet_t ParserResult::getCommand() const {
return cmdcode;
}

int ParserResult::getParamCount() const {
return args.size();
}

int ParserResult::getParamAtIndex(const int index) const {
return args.at(index);
}

// ---------------------- Command Class ---------------------

class Command {
private:
char *cmdstr;
CommandSet_t cmdcode;
public:
static size_t min_cmd_len; // The length of the shortest command (with params)
static size_t max_cmd_len; // The length of the longest command (with params)

Command(const CommandSet_t code, const char* command);
Command(const Command& c);
~Command();
const Command & operator=(const Command &c);
void setCommand(const char* cmd);

char *getCommand() const {
   	return cmdstr;
}

void setCode(const CommandSet_t code) {
   	cmdcode = code;
}

CommandSet_t getCode() const {
   	return cmdcode;
}
};

const Command & Command::operator=(const Command &c) {
if (this == &c)
   	return *this;

delete [] cmdstr;
cmdcode = c.cmdcode;

cmdstr = new char[strlen(c.cmdstr) + 1];
strcpy(cmdstr, c.cmdstr);

return *this;
}

Command::Command(const Command& c) {
cmdcode = c.cmdcode;

cmdstr = new char[strlen(c.cmdstr) + 1];
strcpy(cmdstr, c.cmdstr);
}

Command::Command(const CommandSet_t code, const char* command) {
setCommand(command);
setCode(code);
}

// Static variables representing the shortest and longest commands.
size_t Command::min_cmd_len = INT_MAX;
size_t Command::max_cmd_len = 0;

void Command::setCommand(const char* cmd) {
size_t cmdlen = 0;

// Allocate enough memory to hold the command.
cmdstr = new char[strlen(cmd) + 1];
// Enough space guaranteed above.
strcpy(cmdstr, cmd);

// First find the minimum length for this command.
cmdlen = get_cmd_len(cmdstr, LT_MIN);

if (cmdlen < Command::min_cmd_len)
   	Command::min_cmd_len = cmdlen;

// Find the maximum length.
cmdlen = get_cmd_len(cmdstr, LT_MAX);

if (cmdlen > Command::max_cmd_len)
   	Command::max_cmd_len = cmdlen;
}

Command::~Command() {
// Recycle memory.
delete [] cmdstr;
}

// ----------------- MLParser Class -----------------

// A vector that contains all the available commands.
static vector <Command*> commands;

/**
*  T will be any type of your choice used to return result. The only requirement for T is that it supports
*  STL like iterators using a T::iterator internal type together with T::iterator begin(); and T::iterator::end() methods.
*  T iterators should act as pointers to objects that support the IParserResult interface;
*/
template<class T>
class IParser {
public:
typedef T ParserResultType;
/**
	*  resets/intialises the parser, ready to process an input stream
	*/
virtual void initParser() = 0;
/**
	*  processes any given number of characters from the stream at a time, returns
	*  the sets of identified commands if any.
	*/
virtual T processInput(const char* inputdata, const int inputlen) = 0;
};

// Use a vector of ParserResult objects for the T type.
typedef vector<ParserResult> ParserRes;

class MLParser : public IParser<ParserRes> {
private:
char *cmdbuf;
int bufp, bufsz;
public:
MLParser(const size_t buflen);
MLParser(const MLParser& p);
~MLParser();
const MLParser & operator=(const MLParser &p);
void moveData(const char *loc);
void initParser();
ParserRes processInput(const char* inputdata, const int inputlen);
};

void MLParser::initParser() {
// Initialize parser state.
memset(cmdbuf, 0, bufsz);
bufp = 0;
}

const MLParser & MLParser::operator=(const MLParser& p) {
if (this == &p)
   	return *this;

delete [] cmdbuf;
bufsz = p.bufsz;
bufp = p.bufp;
cmdbuf = new char[bufsz + 1];
memcpy(cmdbuf, p.cmdbuf, bufsz + 1);

return *this;
}

MLParser::MLParser(const MLParser& p) {
bufp = p.bufp;
bufsz = p.bufsz;
cmdbuf = new char[bufsz + 1];
memcpy(cmdbuf, p.cmdbuf, bufsz + 1);
}

MLParser::MLParser(const size_t buflen) {
// Make sure there is adequate buffer space for the available commands.
if (buflen < Command::max_cmd_len) {
   	cerr << "Buffer size too small to handle "
           	"all the available commands\n";
   	exit(1);
}
bufsz = buflen;

// Allocate memory for the buffer.
cmdbuf = new char[buflen + 1];
initParser();
}

/**
* Move data from memory location loc to the start of the buffer.
* 
* @param loc const char *
*/
void MLParser::moveData(const char* loc) {
bufp = strlen(cmdbuf) - (loc - cmdbuf);
memmove(cmdbuf, loc, bufp);
cmdbuf[bufp] = 0;
}

MLParser::~MLParser() {
// Recycle memory.
delete [] cmdbuf;
}

/**
* Process user input detecting commands and parsing arguments.
* 
* @param inputdata const char *
* @param inputlen const int
* @return ParserRes
*/
ParserRes MLParser::processInput(const char* inputdata, const int inputlen) {
ParserRes res;
const char *p;
int left, space, cplen;

// Prevent calls with arguments that could crash the program.
if (inputdata == NULL)
   	return res;

/* Keep looping while there is more data to consume from the input and the
  	buffer length contains more characters than the minimum command. */
for (left = inputlen; left > 0
       	|| (left == 0 && strlen(p) >= Command::min_cmd_len) {

   	// Available space inside the buffer.
   	space = bufsz - bufp;

   	if (left && space) { // If we have data and space available..
       	// Consume input data and fill the buffer.
       	cplen = left > space ? space : left;
       	memcpy(cmdbuf + bufp, inputdata, cplen);

       	// Update pointers to reflect the change.
       	left -= cplen;
       	inputdata += cplen;
       	bufp += cplen;

       	// Null terminate the buffer to facilitate string operations.
       	cmdbuf[bufp] = 0;
   	} else if (left == 0) {
       	// Allows the loop to run one more time.
       	left = -1;
   	}

   	// Set pointer to the start of the buffer.
   	p = cmdbuf;
   	// Used when there is a need to refuel the buffer.
   	bool reload = false;

   	// While there is more data available in the buffer..
   	while (*p) {

       	// Check if we have to refill the buffer with more data.
       	if (strlen(p) < Command::min_cmd_len) {
           	moveData(p);
           	break;
       	}

       	bool valid_cmd;

       	// Iterate through all the available commands to find a match.
       	for (vector<Command*>::iterator i = commands.begin();
               	i != commands.end(); ++i) {
           	// Record command arguments as they become available.
           	char args[Max_args][Max_digits] = { {0} };
           	int argp = 0;

           	const char *t = p;
           	const char *c = (*i)->getCommand();

           	// Suppose we have a valid command.
           	valid_cmd = true;

           	while (*c && *t) {
               	if (*c == '%') {
                   	int min, max, j;

                   	if (!isdigit(*t)) {
                       	valid_cmd = false;
                       	break;
                   	}

                   	if (get_range(++c, &min, &max) || min < 0 || max < 0) {
                       	cerr << "Invalid command format\n";
                       	exit(1);
                   	}

                   	c = strchr(c, ']') + 1;

                   	for (j = 0; j < max && *t && isdigit(*t); j++)
                       	args[argp][j] = *t++;

                   	args[argp][j] = 0;
                   	argp++;

                   	if (j < min) {
                       	valid_cmd = false;
                       	break;
                   	}
               	}

               	if (valid_cmd && !*c)
                   	break;

               	if (valid_cmd && (*c != *t)) {
                   	valid_cmd = false;
                   	break;
               	}

               	c++;
               	t++;
           	}

           	// Currently checking command is longer than the buffer data.
           	if (*c && !*t) {
               	moveData(p);
               	reload = true;
               	break;
           	}

           	if (valid_cmd) { // Add the command to the results vector.
               	res.push_back(ParserResult((*i)->getCode(),
                       	argp, &args[0]));
               	moveData(t);
               	reload = true;
               	if (left == -1) // Check for any commands left over.
                   	left = 0;
               	break;
           	}
       	}

       	if (reload)
           	break;

       	p++;
   	}
}

return res;
}

// ----------------- Main -----------------

typedef MLParser Rs232Parser_t;

#define TEST_BLOCK_BEGIN do {
#define TEST_BLOCK_END } while(0);
#define CHECK(cond) if(!(cond)) { ++errors; \
                             	cout << __LINE__ << ":" \
                             	<< #cond << " failed " \
                             	<< endl; continue; }

int main(int argc, char *argv[]) {
int c;
// Parse data coming from stdin.
bool accept_input = false;
// Length of the buffer used by the parser.
int buffer_length = 0;

opterr = 0;

// Parse arguments and set the appropriate flags.
while ((c = getopt(argc, argv, "ib:")) != -1) {
   	switch (c) {
       	case 'i':
           	accept_input = true;
           	break;
       	case 'b':
           	buffer_length = atoi(optarg);
           	break;
       	case '?':
           	if (optopt == 'b')
               	cerr << "Option -" << char(optopt) <<
               	" requires an argument.\n";
           	else if (isprint(optopt))
               	cerr << "Unknown option '-" << char(optopt) << "'.\n";
           	else
               	cerr << "Unknown option character.\n";
           	return 1;
       	default:
           	abort();
   	}
}

// Add all valid commands to the commands vector. 
commands.push_back(new Command(csSceneGo, "SCENE%[1-4]GO"));
commands.push_back(new Command(csSceneTGo, "SCENE%[1-4]T%[4]GO"));
commands.push_back(new Command(csSceneTLGo, "SCENE%[1-4]T%[4]L%[3]GO"));
commands.push_back(new Command(csSceneNu, "SCENE%[1-4]NU"));
commands.push_back(new Command(csSceneNd, "SCENE%[1-4]ND"));
commands.push_back(new Command(csSceneUp, "SCENE%[1-4]UP"));
commands.push_back(new Command(csSceneDn, "SCENE%[1-4]DN"));
commands.push_back(new Command(csSceneSt, "SCENE%[1-4]ST"));
commands.push_back(new Command(csSequenceGo, "SEQUENCE%[1-3]GO"));
commands.push_back(new Command(csSequenceSt, "SEQUENCE%[1-3]ST"));
commands.push_back(new Command(csSetSlaveChan, "S%[1-3]C%[2]L%[3]GO"));
commands.push_back(new Command(csSetPackChan, "P%[1-3]C%[2]L%[3]GO"));
commands.push_back(new Command(csSetPackDmx, "P%[1-3]D%[2]L%[3]GO"));
commands.push_back(new Command(csSetModuleChan,
       	"M%[1-3]D%[2]C%[2]L%[3]GO"));
commands.push_back(new Command(csSetButtonState, "P%[3]B%[2]S%[2]\r\n"));
commands.push_back(new Command(csSetButtonState, "P%[3]B%[2]S%[2]GO"));

// Use the smallest buffer sz possible which can handle the longest command.
Rs232Parser_t parser(buffer_length ? buffer_length : Command::max_cmd_len);
Rs232Parser_t::ParserResultType result;
Rs232Parser_t::ParserResultType::iterator i;
int errors = 0;

parser.initParser();

TEST_BLOCK_BEGIN
CHECK(result.begin() == result.end())
TEST_BLOCK_END

result = parser.processInput(NULL, 8);

TEST_BLOCK_BEGIN
CHECK(result.begin() == result.end())
TEST_BLOCK_END

result = parser.processInput("SCENE1GO", 8);

TEST_BLOCK_BEGIN
i = result.begin();
CHECK(i != result.end())
CHECK(i->getCommand() == csSceneGo)
CHECK(i->getParamCount() == 1)
CHECK(i->getParamAtIndex(0) == 1)
       	++i;
CHECK(i == result.end())
TEST_BLOCK_END

result = parser.processInput("SCENE1STP1C22L234GO", 19);

TEST_BLOCK_BEGIN
i = result.begin();
CHECK(i != result.end())
CHECK(i->getCommand() == csSceneSt)
CHECK(i->getParamCount() == 1)
CHECK(i->getParamAtIndex(0) == 1)
       	++i;
CHECK(i != result.end())
CHECK(i->getCommand() == csSetPackChan)
CHECK(i->getParamCount() == 3)
CHECK(i->getParamAtIndex(0) == 1)
CHECK(i->getParamAtIndex(1) == 22)
CHECK(i->getParamAtIndex(2) == 234)
       	++i;
CHECK(i == result.end())
TEST_BLOCK_END

result = parser.processInput("SCENE", 5);

TEST_BLOCK_BEGIN
i = result.begin();
CHECK(result.begin() == result.end())
TEST_BLOCK_END

result = parser.processInput("1GO", 3);

TEST_BLOCK_BEGIN
CHECK(i != result.end())
CHECK(i->getCommand() == csSceneGo)
CHECK(i->getParamCount() == 1)
CHECK(i->getParamAtIndex(0) == 1)
       	++i;
CHECK(i == result.end())
TEST_BLOCK_END

result = parser.processInput("~", 1);

TEST_BLOCK_BEGIN
CHECK(result.begin() == result.end())
TEST_BLOCK_END

result = parser.processInput("S", 1);

TEST_BLOCK_BEGIN
CHECK(result.begin() == result.end())
TEST_BLOCK_END

result = parser.processInput("C", 1);

TEST_BLOCK_BEGIN
CHECK(result.begin() == result.end())
TEST_BLOCK_END

result = parser.processInput("E", 1);

TEST_BLOCK_BEGIN
CHECK(result.begin() == result.end())
TEST_BLOCK_END

result = parser.processInput("N", 1);

TEST_BLOCK_BEGIN
CHECK(result.begin() == result.end())
TEST_BLOCK_END

result = parser.processInput("E", 1);

TEST_BLOCK_BEGIN
CHECK(result.begin() == result.end())
TEST_BLOCK_END

result = parser.processInput("3", 1);

TEST_BLOCK_BEGIN
CHECK(result.begin() == result.end())
TEST_BLOCK_END

result = parser.processInput("G", 1);

TEST_BLOCK_BEGIN
CHECK(result.begin() == result.end())
TEST_BLOCK_END

result = parser.processInput("O", 1);

i = result.begin();
TEST_BLOCK_BEGIN
CHECK(i != result.end())
CHECK(i->getCommand() == csSceneGo)
CHECK(i->getParamCount() == 1)
CHECK(i->getParamAtIndex(0) == 3)
       	++i;
CHECK(i == result.end())
TEST_BLOCK_END

//If -i is specified ask user for input and parse it.
if (accept_input) {
   	const int Bufsiz = 8192;
   	char input[bufsiz];
   	ssize_t bread;

   	// Read and parse input from STDIN.
   	while ((bread = read(STDIN_FILENO, input, Bufsiz - 1)) > 0) {
       	result = parser.processInput(input, bread);

       	if (result.begin() == result.end()) {
           	cout << "EMPTY" << endl; // Just to return something.
       	} else {
           	for (i = result.begin(); i != result.end(); i++) {
               	cout << i->getCommand() << " " << i->getParamCount() << " ";

               	for (int cnt = 0; cnt < i->getParamCount(); cnt++)
                   	cout << i->getParamAtIndex(cnt) << " ";
               	cout << endl;
           	}
       	}
   	}

   	cout << "EMPTY" << endl;
   	close(STDOUT_FILENO);
   	close(STDIN_FILENO);
}

// Free space used by the command objects.
for (vector<Command*>::iterator iter = commands.begin();
       	iter != commands.end(); ++iter)
   	delete *iter;

return errors;
}

 

Επόμενο αρχείο είναι το MLParserTester.php, γραμμένο προφανώς σε PHP. Με αυτό έκανα feed στον parser έναν μεγάλο αριθμό από garbage data μαζί με valid commands (με τυχαία arguments) για να βρω τυχόν bugs (όπως και έγινε). Το script δέχεται 3 command line options τα οποία εξηγώ στο .pdf που έχω κάνει attach σε περίπτωση που κάποιος ενδιαφέρεται να το διαβάσει.

 

>
#!/usr/bin/php
<?php
/**
* This script was written in order to test the accuracy of the modelighting
* parser. It generates junk data along with valid commands and arguments
* (all random) and then calls the parser binary passing it the generated 
* data using write()s of random length (the script can even save the sequence 
* and length of each write for debug purposes). This kind of functionality
* may be controlled using command line arguments as described below.
* 
* No args	Generate random data, do not save anything.
* -d     	Generate random data and save everything in debug files.
* -l     	Load all data from debug files instead of generating new.
* -w     	Only recalculate the legth of each write() and save information.
* 
* The script then reads back from the parser the commands that the latter
* detected and compares them to the commands stored while generating random
* data.
* 
* Tested using PHP 5.3.5-1ubuntu7.4 with Suhosin-Patch (cli)
*/

// Always a good idea to set the following for cmd scripts.
ob_implicit_flush();
set_time_limit(0);

// Define command codes.
define('csUnknown', 0);
define('csSceneGo', 1);
define('csSceneTGo', 2);
define('csSceneTLGo', 3);
define('csSceneNu', 4);
define('csSceneNd', 5);
define('csSceneUp', 6);
define('csSceneDn', 7);
define('csSceneSt', 8);
define('csSequenceGo', 9);
define('csSequenceSt', 10);
define('csSetSlaveChan', 11);
define('csSetPackChan', 12);
define('csSetPackDmx', 13);
define('csSetModuleChan', 14);
define('csSetButtonState', 15);

// All of the available valid commands.
$commands = array(
new Command(csSceneGo, "SCENE%[1-4]GO"),
new Command(csSceneTGo, "SCENE%[1-4]T%[4]GO"),
new Command(csSceneTLGo, "SCENE%[1-4]T%[4]L%[3]GO"),
new Command(csSceneNu, "SCENE%[1-4]NU"),
new Command(csSceneNd, "SCENE%[1-4]ND"),
new Command(csSceneUp, "SCENE%[1-4]UP"),
new Command(csSceneDn, "SCENE%[1-4]DN"),
new Command(csSceneSt, "SCENE%[1-4]ST"),
new Command(csSequenceGo, "SEQUENCE%[1-3]GO"),
new Command(csSequenceSt, "SEQUENCE%[1-3]ST"),
new Command(csSetSlaveChan, "S%[1-3]C%[2]L%[3]GO"),
new Command(csSetPackChan, "P%[1-3]C%[2]L%[3]GO"),
new Command(csSetPackDmx, "P%[1-3]D%[2]L%[3]GO"),
new Command(csSetModuleChan, "M%[1-3]D%[2]C%[2]L%[3]GO"),
new Command(csSetButtonState, "P%[3]B%[2]S%[2]\r\n"),
new Command(csSetButtonState, "P%[3]B%[2]S%[2]GO")
);

// The path of the executable that will be passed to proc_open.
define('ML_EXECUTABLE', './ModeLightingParser -i'); // -i to allow input.
// Where to write error output.
define('ERROR_OUTPUT_FILE', 'error-output.txt');
// Debug data including junk data and valid commands.
define('DEBUG_DATA_FILE', 'MLdata.dbg');
// Contains valid commands that are generated.
define('DEBUG_ARRAY_DATA', 'MLarray.dbg');
// Saved sequence of writes also used for debug purposes.
define('DEBUG_ARRAY_WRITES', 'MLwrites.dbg');

/* Register a shutdown function in order to remove the error output file
* specified above (ERROR_OUTPUT_FILE) in case it's empty after the 
* script finishes execution.
*/
register_shutdown_function("cleanup");

// Save test input in a debug file.
$write_debug = false;
// Do not generate random data but load from debug file.
$load_debug = false;
// Only regenrate writes for the loaded debug data.
$only_writes_debug = false;

// Command line parsing.
$shortopts = "dlw";
$longopts = array(
"debug",
"load",
"writes"
);

$options = getopt($shortopts, $longopts);

if (array_key_exists('d', $options))
$write_debug = true;

if (array_key_exists('l', $options))
$load_debug = true;

if (array_key_exists('w', $options))
$only_writes_debug = true;

// Sanity checks and flag alterations.
if (($load_debug && $write_debug) || ($write_debug && $only_writes_debug))
$write_debug = false;

// Provide visual indications to the user regarding enabled flags.
if ($load_debug)
print_console("[*] Loading debug data from file..");

if ($only_writes_debug)
print_console("[*] Recalculating random writes..");

if ($write_debug)
print_console("[*] Writing debug data to file..");

//------------------------ CLASS DEFINITIONS -----------------------------------

/**
* Represents a complete command. 
*/
class Command {

/**
	* String form of a command with arguments.
	* 
	* @var string
	*/
private $cmdstr;

/**
	* Command code from one of the definitions above.
	* 
	* @var integer
	*/
private $cmdcode;

/**
	* Number of occurences of the command (for statistical purposes).
	* 
	* @var integer
	*/
private $occurrences;

public function __construct($cmdcode, $cmdstr) {
   	$this->cmdstr = $cmdstr;
   	$this->cmdcode = $cmdcode;
   	$occurences = 0;
}

public function getCommand() {
   	return $this->cmdstr;
}

public function getCode() {
   	return $this->cmdcode;
}

public function incUsage() {
   	++$this->occurrences;
}

public function getOccurrences() {
   	return $this->occurrences;
}

}

/**
* Convenience class used to manage Command instances.
*/
class CommandManager {

/**
	* A valid Command object.
	* 
	* @var Command
	*/
private $command;

/**
	* Arguments for the associated command object.
	* 
	* @var array
	*/
private $args = array();

public function __construct(Command $cmdob, array $args=array()) {
   	$this->command = $cmdob;
   	$this->args = $args;
}

public function getCommandObject() {
   	return $this->command;
}

public function getArguments() {
   	return $this->args;
}

}

// ------------------------ FUNCTION DEFINITIONS -------------------------------

/**
* Print a message to STDOUT and optionally terminate execution.
* 
* @param string $msg
* @param boolean $fatal 
*/
function print_console($msg='', $fatal=false) {
print ($fatal ? "[FATAL] " : '') . $msg . "\n";

if ($fatal) {
   	exit(1);
}
}

/**
* Remove the log file if it contains no data (empty).
*/
function cleanup() {
$error_file_contents = @file_get_contents(ERROR_OUTPUT_FILE);

if (empty($error_file_contents))
   	@unlink(ERROR_OUTPUT_FILE);
}

/**
* Generate a random sequence of characters of the specified length.
* 
* @param integer $length
* @return string 
*/
function random_string($length=10) {
$ret = '';
$random_chars = "ABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789~!@#$%^&*()%+{}[]";

for ($i = 0; $i < $length; $i++) {
   	$ret .= $random_chars[rand(0, strlen($random_chars) - 1)];
}

return $ret;
}

/**
* Parse a range of the form [x-y] or [x] and return integer values.
* 
* @param string $range
* @param integer $min
* @param integer $max
*/
function get_range($range, &$min, &$max) {
if (strstr($range, '-') !== FALSE) {
   	preg_match("/%\[([0-9]+)-([0-9]+)\]/", $range, $vals);

   	$min = intval($vals[1]);
   	$max = intval($vals[2]);
} else {
   	preg_match("/%\[([0-9]+)\]/", $range, $vals);

   	$min = $max = intval($vals[1]);
}
}

/**
* Generate a random command with random arguments.
* 
* @global array $commands
* @param Command $cmd
* @param string $cmdstr
* @param array $arguments 
*/
function random_command(&$cmd, &$cmdstr, &$arguments) {
global $commands;

$cmdob = $commands[array_rand($commands)];
$args = array();

$command = $cmdob->getCommand();

// Find all the possibilities for an argument.
preg_match_all("/(%\[.*?\])/", $cmdob->getCommand(), $matches);

foreach ($matches[1] as $range) {
   	get_range($range, $min, $max);
   	$minval = pow(10, $min - 1);
   	$maxval = pow(10, $max) - 1;
   	$rnd_arg = rand($minval, $maxval);
   	$args[] = $rnd_arg;

   	$fchar = strpos($command, '%');
   	$lchar = strpos($command, ']') + 1;
   	$len = $lchar - $fchar;

   	$command = substr_replace($command, $rnd_arg, $fchar, $len);
}

// Assign values to the references passed as arguments.
$cmd = $cmdob;
$cmdstr = $command;
$arguments = $args;
}

/**
* Save debug data to the relevant files.
* 
* @param string $output
* @param array $valid_cmds 
*/
function save_debug_data($output, $valid_cmds) {
file_put_contents(DEBUG_DATA_FILE, $output);
file_put_contents(DEBUG_ARRAY_DATA, serialize($valid_cmds));
}

/**
* Read data from a file stream and parse it into commands.
* 
* @param integer $fd
* @param array $received_cmds 
*/
function read_data($fd, &$received_cmds) {
// 8192 was chosen arbitrarily as it is considered a good buffer value.
$ret = fread($fd, 8192);

// Skip it if it's empty.
if (!empty($ret) && $ret !== FALSE) {
   	$vals = explode("\n", $ret);
   	foreach ($vals as $line) {
       	if (!empty($line) && $line != 'EMPTY')
           	$received_cmds[] = $line;
   	}
}

return $ret;
}

/**
* Randomly calcuate how many bytes to write on each loop. This allows us
* to save this information in case it's needed later for debugging.
* 
* @param string $output
* @return array 
*/
function calculate_writes($output) {
$outp = 0;
$writes = array();

$outp_len = strlen($output);

while ($outp < $outp_len) {
   	$max = $outp_len - $outp < 256 ? $outp_len - $outp : 256;
   	$btw = rand(1, $max);
   	$writes[] = $btw;
   	$outp += $btw;
}

return $writes;
}

//---------------------------------------------------------------------
//--------------- MAIN SCRIPT FUNCTIONALITY STARTS HERE ---------------
//---------------------------------------------------------------------
// The generated or restored (from a backup) output data.
$output = '';
// Save commands while the script generates them.
$valid_cmds = array();
// An array of all the commands received.
$received_cmds = array();

if ($load_debug) { // Load data from debug files.
$output = file_get_contents(DEBUG_DATA_FILE);
$valid_cmds = unserialize(file_get_contents(DEBUG_ARRAY_DATA));
} else { // Generate new random data for this run.
for ($i = 0; $i < 50000; $i++) {
   	$rndfactor = rand(1, 10);

   	if ($rndfactor > 7) {
       	$cmd = null;
       	$cmdstr = '';
       	$args = array();
       	random_command($cmd, $cmdstr, $args);
       	$cmd->incUsage();
       	$valid_cmds[] = array('cmd' => $cmd, 'cmdstr' => $cmdstr, 'args' => $args);

       	$output .= $cmdstr;
   	} else {
       	$output .= random_string(rand(1, 200));
   	}
}
}

// Load writes sequence or recalculate them based on command line options.
if ($load_debug && !$only_writes_debug)
$writes = unserialize(file_get_contents(DEBUG_ARRAY_WRITES));
else
$writes = calculate_writes($output);

if ($write_debug) {
save_debug_data($output, $valid_cmds);
file_put_contents(DEBUG_ARRAY_WRITES, serialize($writes));
}

if ($only_writes_debug)
file_put_contents(DEBUG_ARRAY_WRITES, serialize($writes));

// This is used for the proc_open command below.
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", ERROR_OUTPUT_FILE, "w")
);

// Run the process.
$process = proc_open(ML_EXECUTABLE, $descriptorspec, $pipes);

if (is_resource($process)) { // Proceed only if we have a valid resource.
$outp = 0;

//set_socket_blocking($pipes[1], 0);
// Do the actual writing of data.
foreach ($writes as $wr_len) {
   	$data = substr($output, $outp, $wr_len);
   	$outp += $wr_len;
   	fwrite($pipes[0], $data, strlen($data));
   	fflush($pipes[0]);

   	read_data($pipes[1], $received_cmds);
}

fclose($pipes[0]);

// Just to make sure that we are not missing anything here.
while (read_data($pipes[1], $received_cmds))
       	;

//read_data($pipes[1], $received_cmds);
// Always close open files.	
fclose($pipes[1]);

// Obtain the return value.
$return_value = proc_close($process);
}

// If we encoutered an error simply terminate with an error.
if ($return_value)
exit($return_value);

$failed = false;

// Ensure that the number of generated commands is the same with the received.
if (($count = count($valid_cmds)) != count($received_cmds)) {
print_console("Sent commands differ from the received ones "
       	. $count . " " . count($received_cmds), false);
$failed = true;
}

// Additionally check all the arguments for each command to ensure validity.
for ($i = 0; $i < $count; $i++) {
$cmdob = $valid_cmds[$i]['cmd'];
$cmdcode = $cmdob->getCode();
$argnum = count($valid_cmds[$i]['args']);
$args = $valid_cmds[$i]['args'];

$cmdinfo = explode(' ', $received_cmds[$i]);

if ($cmdcode != $cmdinfo[0])
   	print_console("Command codes do not match");
if ($argnum != $cmdinfo[1])
   	print_console("Arguments count do not match");
for ($j = 0; $j < $argnum; $j++) {
   	if ($args[$j] != $cmdinfo[2 + $j]) {
       	print_console("Arguments do not match "
               	. $args[$j] . " " . $cmdinfo[2 + $j]
               	. " -> " . $valid_cmds[$i]['cmdstr']);

       	print_r($args);
       	print_r($cmdinfo);
   	}
}
}

if ($failed)
exit(1);
?>

 

Επίσης έχω κάνει attach ένα .zip με όλα τα αρχεία για όποιον θέλει να τρέξει τον parser ή να διαβάσει το .pdf...

Περιμένω τα σχόλια σας!! :)

ModeLightning_DiAvOl.zip

Δημοσ.

@DiAvOl

 

αν επιτρεπεται ποσα χρονια ασχολεισαι με τον προγραμματισμο γενικοτερα?

 

Επαγγελματικά περίπου 5-6 χρόνια με διάφορες γλώσσες προγραμματισμού...

 

Έχει κανείς καμιά ιδέα γιατί μου χαλάν το indentation τα code tags? :(

 

By the way, το feedback που έλαβα από την εταιρία:

 

This exercise is based on a real problem we have, and the real grammar for our products RS-232 remote control command set. As stated in the challenge, keep in mind that this parser could be run on a resource constrained environment but should still be able to process a huge amount of data.

 

We take 'resource constrained' very seriously and tend to shy away from implementations requiring too many memory allocations (but for the mandatory iterator and results), costly memcpy/memmove, strcmp/strlen operations and that employ any algorithm running in anything more than O(log n) amortized time. As such we test code submissions with two streams of data both begin around 1Mb, one being 100% correct, the other containing broken tokens and garbage characters. We feed these streams to the submitted code 3 times, as a single block, as single tokens and character per character.

 

The best solution we had so far fits in 400 lines of code, processes the input stream in place (no memcpy/memmove involved, no buffer allocation involved) and runs in O(n) amortized time, making no use of costly strcmp/strlen and being resilient to incorrect streams (i.e. containing invalid tokens, and garbage characters). It also passes 90% of our tests. Our production code fits in 180 lines of code, is completely dynamic (the grammar can be altered on the fly) processes the stream in place in O(1) amortized time without requiring any extra allocation and is resilient to incorrect streams.

 

We felt your code was 'over-engineered' in some ways (why implement operator= & copy constructor for the parser and commands where using the no-copy idiom would have been sufficient?) and not enough in others: your parsing algorithm makes heavy use of memmove/memcpy/strchr and strlen and contains 3 nested inner-loops where a simple lexer should suffice.

 

We appreciate the fact that you spent time working on the exercise and are very thankful for that. We had worse submissions than yours, some plagued with memory leaks and running in O(n3) time, but also better. As said above, we take 'resource constrained' very seriously: Our main controller is running on a 256Mhz processor with 128Mb of memory whilst still being able to drive your lights, talk to your iphone, display a floorplan on your iPad and turn your A/V system on and off.

Δημοσ.

Προσωπικά είμαι δύσπιστος απέναντι σε τέτοιου είδους διαγωνισμός εύρεσης εργασίας. Το μόνο σίγουρο είναι ότι κάποιος ζητά μέσο ενός challenge να λυθεί με όσο το δυνατόν πιο βέλτιστο τρόπο ένα "πρόβλημα" από εκεί και πέρα, οι συμμετέχοντες στέλνουν τις λύσεις τους, κάποιος διαβάζει αυτόν τον κώδικα.. μαθαίνει κάτι από αυτόν και ύστερα τρέχα γύρευε. Δεν λέω ότι συμβαίνει κάτι ανάλογο εδώ για παράδειγμα αλλά δεν ζούμε και σε κοινωνία αγγέλων..

 

+1 στον DiAvOl για την προσπάθεια του, από εμένα!!

Δημοσ.

Προσωπικά είμαι δύσπιστος απέναντι σε τέτοιου είδους διαγωνισμός εύρεσης εργασίας. Το μόνο σίγουρο είναι ότι κάποιος ζητά μέσο ενός challenge να λυθεί με όσο το δυνατόν πιο βέλτιστο τρόπο ένα "πρόβλημα" από εκεί και πέρα, οι συμμετέχοντες στέλνουν τις λύσεις τους, κάποιος διαβάζει αυτόν τον κώδικα.. μαθαίνει κάτι από αυτόν και ύστερα τρέχα γύρευε. Δεν λέω ότι συμβαίνει κάτι ανάλογο εδώ για παράδειγμα αλλά δεν ζούμε και σε κοινωνία αγγέλων..

 

Συγγνώμη που σε αρπάζω αλλά... μόνο Έλληνας θα το σκεφτόταν έτσι :( (θα έχετε προσέξει ότι δεν τα πάω πολύ καλά με τους Έλληνες).

 

Δεν αντέχω αυτή τη νοοτροπία που έχουμε αποκτήσει σ' αυτή τη χώρα. Δηλαδή ούτε λίγο ούτε πολύ υποθέτεις (γιατί αν δεν είναι έτσι τότε δεν ισχύει το σκεπτικό) ότι:

 

1. "Ο κάθε ένας" που θα απαντήσει στην αγγελία είναι καλύτερος από τους τύπους που είναι εκεί και το κάνουν αυτό σαν επάγγελμα (αλλιώς δε θα είχαν να "μάθουν" τίποτα). Δε θέλω να σε στεναχωρήσω, αλλά σχεδόν σίγουρα είναι καλύτεροι απο σένα. Αν έχεις αμφιβολία, δοκίμασε να κάνεις implementation τόσο καλό όσο αυτό που περιγράφουν.

2. Η αγγελία είναι ψευδής (αλλιώς τι εννοείς "τρέχα γύρευε"). Δηλαδή δεν έχουν καλύτερο τρόπο να εκπαιδεύσουν το προσωπικό τους παρά να βγάζουν κάθε τόσο μια αγγελία-άσκηση για να παίρνουν διδακτικό υλικό. Φοβερή σύλληψη, κι ας αφήσουμε τα άλλα τα κορόιδα να παιδεύονται με βιβλία, με γράψιμο κώδικα μόνοι τους, με διάβασμα open source κώδικα, με συνεργασίες με αξιόλογους developers κλπ.

3. Ακόμα κι αν όλα αυτά είναι ακριβώς έτσι, η εταιρία είναι επιπλέον αρκετά χαζή ή/και κουτοπόνηρη ούτως ώστε αν βρεθεί κάποιος που όντως τους δώσει super duper διαστημικό καλύτερο απ' ότι ποτέ θα μπορούσαν να φανταστούν στα πιο τρελά τους όνειρα implementation, δε θα τον προσλάβουν και πάλι. Πολύ λογικό, εξάλλου ποιός θα ήθελε ένα τέτοιο άτομο να δουλεύει για λογαριασμό του;

 

Λυπάμαι που στο λέω στα ίσια, αγαπητέ Έλληνα (δεν αναφέρομαι σε σένα προσωπικά directx), αλλά το σύμπαν δεν περιστρέφεται γύρω απο σένα.

 

Μετα συγχωρήσεως.

 

ΥΓ: Καταλαβαίνω ότι θα μπορούσε, σε κάποια σπάνια περίπτωση, να συμβεί αυτό ακριβώς που λες. Αλλα τι συμπέρασμα βγαίνει απ' αυτό; Ότι επειδή ο ένας μανάβης στους 1000 μπορεί να σε κλέβει το λογικό συμπέρασμα είναι να πηγαίνεις σε κάθε μανάβη με δική σου ζυγαριά;

 

ΥΓ2: Θα προσπαθήσω αν βρω χρόνο να κάνω και γω ένα implementation.

Δημοσ.

Συγγνώμη που σε αρπάζω αλλά... μόνο Έλληνας θα το σκεφτόταν έτσι :( (θα έχετε προσέξει ότι δεν τα πάω πολύ καλά με τους Έλληνες).[..]

 

Επί προσωπικών (συγκαλυμμένων) επαναλαμβανόμενων (;)) επιθέσεων δεν απαντώ._

 

Αρκούμε στην τελική σου και ουσιαστική παρατήρηση:

 

ΥΓ: Καταλαβαίνω ότι θα μπορούσε, σε κάποια σπάνια περίπτωση, να συμβεί αυτό ακριβώς που λες. Αλλα τι συμπέρασμα βγαίνει απ' αυτό; Ότι επειδή ο ένας μανάβης στους 1000 μπορεί να σε κλέβει το λογικό συμπέρασμα είναι να πηγαίνεις σε κάθε μανάβη με δική σου ζυγαριά;

 

Το συμπέρασμα που βγαίνει είναι ότι θα πρέπει να είσαι προσεκτικός με τον μανάβη σου, τίποτε περισσότερο και τίποτε λιγότερο από αυτό.

 

Για εμένα η κουβέντα τελειώνει εδώ.

 

Καλή συνέχεια._

Δημοσ.

Και αμα σχεδον σιγουρα ολοι αυτοι ειναι καλύτεροι απο σενα γιατι ψάχνουν υπαλλήλους?

 

Δεν νομιζω να τους καιει τοσο ωστε να βρουν δουλεια στους κακομοιρους τους νεους που μαστιζονται

 

απο την ανεργια :S Σε ελεγχουν να δουν ποσο καλος εισαι ωστε αν γινεις μελος της ομαδας τους

 

να μπορεις να συμβαδίσεις μαζι τους ή μπορει απλα να ψάχνουν ενα ατομο που έχει ενα συγκεκριμενο skill

το οποιο λειπει απο τους αλλους . Τι θα πει ολοι ειναι καλυτεροι απο σενα... σε ολα? δεν το πιστευω αυτο

χωρις να το λεω αλαζονικα. ΕΚ της λογικης δεν υποστηριζεται.

 

ΤΩρα αν οι αγγελιες ειναι ψευδείς η οχι... ε νταξει αν νομιζεις οτι ειναι απλα δεν συμμετεχεις.

Τοσοι απατεωνες κυκλοφορουν στο ιντερνετ καθημερινα μωρε αυτο μας πειραξε :D σιγα το νεο !

Ειναι δικη σου ευθυνη να καταλαβεις αν σε κοροιδευει ο άλλος η οχι.

 

p.s Συγχαρητηρια και απο μενα στον DiAvOl εκανε καλη δουλεια !

Δημοσ.

Defacer εγω διαφωνω κάθετα με τον συγκεκριμένο τρόπο. Θα σου εξηγήσω. Ποιος μου λέει εμένα ότι αυτός που έγραψε την άσκηση την εγραψε μόνος και δεν είχε βοήθεια. Μπορεί να μην ήταν βοήθεια κώδικα αλλά απλά μια κατευθυνση απο έναν πολύ έμπειρο ποια προσέγγιση να ακολουθήσει.

 

Παράδειγμα μπορεί να καθόμασταν όλη η ομάδα του insomnia και να σκεφτόμασταν ή να επαιρνά τον κολλητό μου expert σε C και embedded systems με δεκα χρόνια προυπηρεσία και να με βοηθούσε. Τι τρόπος πρόσληψης τελείως απρόσωπος είναι αυτός?

 

Μπραβο στο παλικάρι και απο μένα !!!

Δημοσ.

Defacer εγω διαφωνω κάθετα με τον συγκεκριμένο τρόπο. Θα σου εξηγήσω. Ποιος μου λέει εμένα ότι αυτός που έγραψε την άσκηση την εγραψε μόνος και δεν είχε βοήθεια. Μπορεί να μην ήταν βοήθεια κώδικα αλλά απλά μια κατευθυνση απο έναν πολύ έμπειρο ποια προσέγγιση να ακολουθήσει.

 

Παράδειγμα μπορεί να καθόμασταν όλη η ομάδα του insomnia και να σκεφτόμασταν ή να επαιρνά τον κολλητό μου expert σε C και embedded systems με δεκα χρόνια προυπηρεσία και να με βοηθούσε. Τι τρόπος πρόσληψης τελείως απρόσωπος είναι αυτός?

 

Μπραβο στο παλικάρι και απο μένα !!!

 

 

Και ειναι κακο αυτο? Εμενα μου δειχνει ενα ατομο που μπορει να συνεργαστει

με τους γυρω του εφοσον τους πειθει να τον βοηθησουν. Αμα σου βγαζει τη δουλεια

σε τι θα σε πειραζε εσενα? :D

Δημοσ.

Για τον εργοδότη δεν με νοιάζει το θέμα είναι ότι ο διαγωνισμός δεν είναι ισοτιμος και απλα δεν μπορείς να το ξέρεις

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα

  • Δημιουργία νέου...