; higher-order string functions

(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))))))

(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))

(display (caesar 3 "PROGRAMMING praxis")) (newline)
(display (caesar -3 "SURJUDPPLQJ sudalv")) (newline)

(define (string-for-each proc str)
  (do ((i 0 (+ i 1)))
      ((= i (string-length str)))
    (proc (string-ref str i))))

(string-for-each
  (lambda (c) (display (char->integer c)) (newline))
  "PRAXIS")

(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)))))

(display (string-fold cons '() "PRAXIS")) (newline)

(display (string-fold (lambda (c count)
                        (if (char-lower-case? c)
                            (+ count 1)
                            count))
                      0 "Programming Praxis"))
(newline)

(string-fold (lambda (c junk)
               (display (char->integer c)) (newline))
             0 "PRAXIS")
                 
(display (list->string ; double characters
    (string-fold-right (lambda (c base)
                         (cons c (cons c base)))
                       '() "PRAXIS")))
(newline)