5 ms·
IMO this is no small part of why C still has its reputation for speed. Think how you'd write this in C. Maybe /* Recursively removes doublets from input.
by edflsafoiewq 6y ago
IMO this is no small part of why C still has its reputation for speed. Think how you'd write this in C. Maybe
/*
Recursively removes doublets from input. A doublet is a pair of adjacent chars,
identical or differing only in capitalization (eg. "aa" or "aA"). The result is
written to output, which must be at least input_length bytes long.
Returns the number of bytes in the result.
*/
size_t remove_doublets(char * output, const char * input, size_t input_length);
and the body is a dozen lines of pointer pushing and tolower. Do you faff around with malloc? unicode case conversions? hashmaps??? Of course not, that would be too hard, so you just write the stupid C code. For all its faults, the C idiom really does push you towards the simple, stupid answer to code like this.
- ziml77 6y agoThat's also a problem with C though. Because the complex answer is such a pain to write, people go for the simple answer that doesn't handle all the cases that it actually should. Like ignoring Unicode case conversions means that it will only work as expected on English input. Really, no matter what the language is, people need to actually put some thought into what they're doing.
- brundolf 6y agoBoth good points. C gives you rope to hang yourself with when it comes to correctness; Rust gives you rope to hang yourself with when it comes to performance. Personally I prefer the latter, but awareness and understanding of what's really going on is important no matter what language you're using.
- bobthebuilders 6y agoThe other thing is say algorithm selection. Some data structures may be more performant for the job at hand, but do you really want to package or build them yourself? A lot of the time the answer is no.
- higerordermap 6y agoAmong new languages, zig aims to make all allocations obvious.
- ahava_yeshua 6y agoI thought of 2 ways to do this in C with plain iteration. Both of course involve pointers but one uses a stack while the other keeps track of pointers. I was more interested in finding the doublets and returning them than returning the rest (which is trivial given the former). int process (char input, char output) { int result = 0; char p = input; char prev, next, out = output; if (NULL == input) return -1; next = p + 1; while (next){ / Test for (A,a) | (B,b) | (a,A) | (b,B).. etc. in the input string. This macro checks for this condition. / if (OPP_POLARITY(p, next)){ out++ = p; out++ = next++; / back up one spot for next check. Its possible there is a lurking combination between new p and new next. / if (p != input) --p; else p = next; result++; }else{ p = next; next = p + 1; } } // terminate out out = '\0'; return result; } and then the stack based one: int stack_process (char input, char output) { /* Use a stack to keep track of correct index / typedef int wr_index; wr_index wr_idx = 0; int output_len = 20; char dbg_str[20]; #define PUSH(val)\ do{\ if (wr_idx < output_len)\ output[wr_idx++] = (val);\ }while(0) #define POP(lvalue)\ do{\ if (wr_idx > 0)\ (lvalue) = output[--wr_idx];\ }while(0) #define PEEK(n)\ output[wr_idx - (n + 1)] \ #define GET_COUNT()\ wr_idx\ char p = input; char out = dbg_str; int result = 0; / This avoids having to check if there are atleast 2 items on the stack in the loop below. / PUSH(p++); while(p){ PUSH(p++); if (OPP_POLARITY(PEEK(0), PEEK(1))){ POP(*out); out++; POP(*out); out++; result++; } } *out ='\0'; return result; } dbg_str is a throw-away string to check results. Oh, and the macro OPP_POLARITY is defined as: #define OPP_POLARITY(x,y)\ (abs((x) - (y)) == ('a' - 'A')) Keep in mind, I have not touched C for a little over a decade. For some reason, this problem reminded me of C and I thought I would have a go at it.