[racket] Super basic question about strings

From: Neil Van Dyke (neil at neilvandyke.org)
Date: Wed Nov 17 10:16:56 EST 2010

For new students, I would show them examples like Eli and Matthias did.  
For production work, I usually build up strings using string ports.

Compared to various ways of building lists and doing "string-append", or 
to simply doing lots of incremental "string-append" or "format", using a 
string port usually permits more tail calls, and sometimes reduces 
allocations or otherwise inefficient calls.  (Note that I assume that 
the string port implementation itself is very efficient internally.)

For example, a first attempt using string ports, without performance tuning:

(define (alist->string alist)
  (if (null? alist)
      (let ((os (open-output-string)))
        (let loop ((alist alist))
          (write      (caar alist) os)
          (write-char #\=          os)
          (write      (cdar alist) os)
          (if (null? (cdr alist))
              (begin0 (get-output-string os)
                (close-output-port os))
              (begin (write-char #\space os)
                     (loop (cdr alist))))))))

The string ports example is a lot more verbose than Matthias's 
string-join/map/format example, but in a couple quick profiles of 
100,000 iterations just now, on an alist of only 5 elements, the string 
ports example ran in 74%-76% of the time.  In a couple additional 
profiles, this performance edge seems to increase with the length of the 
alist and/or length of the output string.

For programs that are sufficiently fast, this particular performance 
edge is insignificant.  But when you're building large production 
systems, I've seen attention to grungy low-level performance details in 
Racket result in boosts of 2x, 10x, 50x...  That makes a big difference 
when you want your Web site to respond in a fraction of a second under 
load or you're buying/renting application servers.


Posted on the users mailing list.