Path: csiph.com!weretis.net!feeder8.news.weretis.net!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c Subject: Re: Programming exercise/challenge Date: Thu, 10 Dec 2020 01:49:58 -0800 Organization: A noiseless patient Spider Lines: 189 Message-ID: <86wnxq2do9.fsf@linuxsc.com> References: <86wnxwkyol.fsf@linuxsc.com> <871rg2rffu.fsf@bsb.me.uk> <86v9dehts2.fsf@linuxsc.com> <87360hq0si.fsf@bsb.me.uk> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: reader02.eternal-september.org; posting-host="e123793f93e194ce9596f9b3807e7692"; logging-data="2088"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+hhMe/KAr/93+Ta7TrJRBko0QE7/mLotc=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:7FGx6+zg+3Jlw2EnDcXztkgJuAU= sha1:CMGzRk7vPMgu2WLLj2bNUeXMBX4= Xref: csiph.com comp.lang.c:157185 Ben Bacarisse writes: > Tim Rentsch writes: [...] >>> Haskell has two big advantages for this exercise: pattern matched >>> cases and lazy evaluation. [..explanation..] >> >> Also one not mentioned: 'input <- getContents'. An analogous >> functionality in C would read the contents of a file into a >> character array, which would make the C program easier. > > I don't think its analogous at all! 'input' <- getContents does > no IO. Input is fetched only as needed to perform computations on > 'input' and processed data will be considered garbage. The point I am trying to make here is not about IO but about data structuring. I understand that Haskell has lazy eval and agree that there are advantages that accrue from that. Here though I think the important property is that getContents returns a "data structure" that is convenient to process in Haskell, specifically in the context of what an uncommenting program wants to do. An analogous data structure in C (not counting the lazy evaluation part, which I've already acknowledged) is a character array. Let's look at some C code that takes advantage of its primary data structure for handling strings. ** SPOILER ALERT ** The program below is a comment remover where backslant-newline sequences have also been eliminated in the output. Stop reading now if you don't want to see the code yet. ** SPOILER ALERT ** Granted, the code below isn't a nice as your Haskell version, but I think it isn't too bad. How does it strike you? /* remove comments after pre-combining physical lines into logical lines */ #include #include #include typedef const char *String; static int decomment_stdin_all_good( void ); static void splice_lines( char * ); static int decomment_okay( String ); static String show_literal( String ); static String show_string( String ); static String handle_slant( String ); static String skip_over_literal( String ); static String skip_over_string( String ); static String skip_comment_tail( String ); static char * contents_of_file( FILE * ); static String skip_to( String, String ); int main(){ return ! decomment_stdin_all_good() ? fputs( " [not good] ", stderr ), EXIT_FAILURE : 0; } int decomment_stdin_all_good(){ char *in = contents_of_file( stdin ); return in ? splice_lines( in ), decomment_okay( in ) : 0; } void splice_lines( char *s ){ char c, *p = s; const char *q = s; while( c = *p++ = *q++ ){ if( c == '\\' && 0[q] == '\n' ) p--, q++; } } /* process spliced input, removing comments */ int decomment_okay( String s ){ while( s && *s ){ String p = skip_to( s, "\"'/" ); printf( "%.*s", (int){ p-s }, s ); /**/ if( *p == '\'' ) s = show_literal( p ); else if( *p == '"' ) s = show_string( p ); else if( *p == '/' ) s = handle_slant( p ); else s = p; } return s ? 1 : 0; } String show_literal( String s ){ String p = skip_over_literal( s+1 ); return printf( "%.*s", (int){p-s}, s ), p ; } String skip_over_literal( String s ){ String p = skip_to( s, "\n'\\" ); return *p == '\n' || !*p ? p : *p++ == '\'' ? p : *p == '\n' || !*p ? p : skip_over_literal( p+1 ); } String show_string( String s ){ String p = skip_over_string( s+1 ); return printf( "%.*s", (int){p-s}, s ), p; } String skip_over_string( String s ){ String p = skip_to( s, "\n\"\\" ); return *p == '\n' || !*p ? p : *p++ == '"' ? p : *p == '\n' || !*p ? p : skip_over_string( p+1 ); } String handle_slant( String s ){ return 1[s] == '*' ? putchar( ' ' ), skip_comment_tail( s+2 ) : 1[s] != '/' ? putchar( '/' ), s+1 : skip_to( s+1, "\n" ); } String skip_comment_tail( String s ){ String p = strstr( s, "*/" ); return p ? p+2 : strchr( s, 0 ); } /*** utility functions ***/ char * contents_of_file( FILE *f ){ typedef unsigned long Index, Extent; char *r = malloc( 1 ); Index k = 0; Extent n = 1; int c; if( !r ) return r; r[k] = 0; while( c = getc( f ), c != EOF ){ if( k+1 >= n ){ Extent n1 = n + n/4 + 1024; char *r1 = realloc( r, n1 ); if( !r1 ) return free( r ), r = 0; r = r1, n = n1; } r[k++] = c, r[k] = 0; } return r; } inline String skip_to( String s, String choices ){ return s + strcspn( s, choices ); }