Higher-Order String Functions
January 26, 2016
string-map
returns a newly-allocated string:
(define (string-map proc str) (let* ((len (string-length str)) (out (make-string len))) (do ((i 0 (+ i 1))) ((= i len) out) (string-set! out i (proc (string-ref str i))))))
For example, here is a simple Caesar cipher:
(define (shift c n) (cond ((char-upper-case? c) (integer->char (+ (modulo (+ (char->integer c) -65 n) 26) 65))) ((char-lower-case? c) (integer->char (+ (modulo (+ (char->integer c) -97 n) 26) 97))) (else c)))
(define (caesar n str) (string-map (lambda (c) (shift c n)) str))
> (caesar 3 "PROGRAMMING praxis") "SURJUDPPLQJ sudalv" > (caesar -3 "SURJUDPPLQJ sudalv") "PROGRAMMING praxis"
string-for-each
is simpler than string-map
because it doesn’t need a temporary string:
(define (string-for-each proc str) (do ((i 0 (+ i 1))) ((= i (string-length str))) (proc (string-ref str i))))
And here’s a simple example:
> (string-for-each (lambda (c) (display (char->integer c)) (newline)) "PRAXIS") 80 82 65 88 73 83
The two string-fold
functions are the most interesting:
(define (string-fold proc base str) (let loop ((base base) (i 0)) (if (= i (string-length str)) base (loop (proc (string-ref str i) base) (+ i 1)))))
(define (string-fold-right proc base str) (let loop ((base base) (i (- (string-length str) 1))) (if (negative? i) base (loop (proc (string-ref str i) base) (- i 1)))))
The folds can be used for many tasks:
> (string-fold cons '() "PRAXIS") ; reverse string->list (#\S #\I #\X #\A #\R #\P) > (string-fold (lambda (c count) ; count lower-case chars (if (char-lower-case? c) (+ count 1) count)) 0 "Programming Praxis") 15 > (string-fold (lambda (c junk) ; string for-each (display (char->integer c)) (newline)) 0 "PRAXIS") 80 82 65 88 73 83 > (list->string ; double characters (string-fold-right (lambda (c base) (cons c (cons c base))) '() "PRAXIS")) "PPRRAAXXIISS"
You can run the program at http://ideone.com/4iMCaC.
My scheme version. I probably should’ve used make-string.
Well this is one useless exercise in Common Lisp: in Common Lisp, strings are vectors, and vectors are sequences, so all the vectors and the sequence operations apply to strings. map, map-into, reduce, reverse, length, equal, etc… So the solution of this problem in CL, is null, apart perhaps from the renaming (which is dumb, and caused the problem in scheme in the first place; use generic functions!):