Next: , Previous: , Up: strings   [Index]


25.9 Fold and unfold

Function: string-fold-left kons knil str0 str ...
Function: string-fold-right kons knil str0 str ...

The fundamental string iterator. The string arguments must have the same length.

kons is iterated left–to–right over each index in all of the strings, stopping at the end of the shortest; kons is applied as:

(kons idx state
  (string-ref str0 idx)
  (string-ref str  idx)
  ยทยทยท)

where state is the current state value; the current state value begins with knil, and becomes whatever kons returned at the respective iteration; idx is the current index.

string-fold-right is similar to string-fold, but it iterates right–to–left.

Notice that to allow for an unspecified number of arguments, these folds hand the state as first argument to kons, as opposed to the usual fold arguments.

Function: string-fold-left* kons knil str0 str ...
Function: string-fold-right* kons knil str0 str ...

Like string-fold and string-unfold but accept strings of different length, iterating until the end of the shortest one.

Function: %substring-fold-left kons knil str start past
Function: %substring-fold-right kons knil str start past
Macro: string-fold kons knil S
Macro: string-fold-right kons knil S

Fundamental iterators for substrings. kons is iterated over each character of the selected substring:

(kons
  (string-ref str (+ start idx))
  state)

where state is the current state value; the current state value begins with knil, and becomes whatever kons returned at the respective iteration; idx is the current index.

The left–fold iterator, %substring-fold-left, builds the return value as:

(kons
  (string-ref str (- past 1))
  (kons
    (string-ref str (- past 2))
    ...
      (kons
        (string-ref str (+ start 2))
        (kons
           (string-ref str (+ start 1))
           (kons
              (string-ref str start)
              knil)))))

The right–fold iterator, %substring-fold-right, builds the return value as:

(kons
  (string-ref str start
  (kons
    (string-ref str (+ start 1))
    ...
      (kons
        (string-ref str (- past 3))
        (kons
           (string-ref str (- past 2))
           (kons
              (string-ref str (- past 1))
              knil)))))

Examples:

;; Convert a string to a list of chars.
(substring-fold-left cons '() "abcd")
⇒ (#\d #\c #\b #\a))

;; Count the number of upper-case characters in a string.
(substring-fold-left (lambda (c count)
                       (if (char-upper-case? c)
                           (+ count 1)
                         count))
                     0
                     "ABCdefGHi")
⇒ 5

;; Double every backslash character in S.
(let* ((str "abc\\de\\f\\ghi")
       (ans-len (string-fold
                 (lambda (c sum)
                   (+ sum (if (char=? c #\\) 2 1)))
                 0 str))
       (ans (make-string ans-len)))
  (substring-fold-left
   (lambda (c i)
     (let ((i (if (char=? c #\\)
                  (begin
                    (string-set! ans i #\\)
                    (+ i 1))
                i)))
        (string-set! ans i c)
        (+ i 1)))
    0 str)
   ans)
⇒ "abc\\\\de\\\\f\\\\ghi"
Function: string-unfold stop? seed->char make-seed first-seed
Function: string-unfold stop? seed->char make-seed first-seed base-string
Function: string-unfold stop? seed->char make-seed first-seed base-string make-final

This is a fundamental constructor for strings. Arguments description follows.

make-seed

A map function used to generate a series of “seed” values from the initial seed:

first-seed
(make-seed first-seed)            ⇒ seed2
(make-seed seed2)                 ⇒ seed3
(make-seed seed3)                 ⇒ seed4
...
stop?

A predicate function telling when to stop generating characters; when it returns true when applied to one of the seed values.

seed->char

Map function mapping each seed value to the corresponding character in the result string. These chars are assembled into the string in a left–to–right order.

base-string

An optional string which is used as initial/leftmost portion of the constructed string. Defaults to the empty string.

make-final

Optional function applied to the terminal seed value (on which stop? returns true) to produce the final/rightmost portion of the constructed string. Defaults to (lambda (x) "").

More precisely, the following (simple, inefficient) definitions hold:

;; iterative
(define (string-unfold stop? seed->char make-seed
                       first-seed base-string make-final)
  (let loop ((seed    first-seed)
             (result  base-string))
    (if (stop? seed)
        (string-append result (make-final seed))
      (loop (make-seed seed)
            (string-append result
                           (string (seed->char seed)))))))

;; recursive
(define (string-unfold stop? seed->char make-seed
                       first-seed base-string make-final)
  (string-append
     base-string
     (let loop ((seed first-seed))
       (if (stop? seed)
           (make-final seed)
         (string-append (string (seed->char seed))
                        (loop (make-seed seed)))))))

This function is a fairly powerful string constructor; we can use it to convert a list to a string, read a port into a string, reverse a string, copy a string, and so forth. Examples:

(port->string p) = (string-unfold eof-object? values
                                  (lambda (x) (read-char p))
                                  (read-char p))

(list->string lis) = (string-unfold null? car cdr lis)

(string-tabulate f size) = (string-unfold (lambda (i)
                                            (= i size))
                                          f add1 0)

to map proc over a list lis, producing a string:

(string-unfold null? (compose proc car) cdr lis)

Interested functional programmers may enjoy noting that string-fold-right and string-unfold are in some sense inverses. That is, given operations knull?, kar, kdr, kons, and knil satisfying:

(kons (kar x) (kdr x)) ⇒ x
(knull? knil) ⇒ #t

then:

(string-fold-right kons knil
                   (string-unfold knull? kar kdr x))
⇒ x

and:

(string-unfold knull? kar kdr
               (string-fold-right kons knil s))
⇒ s

The final string constructed does not share storage with either base-string or the value produced by make-final.

Function: string-unfold-right stop? seed->char make-seed first-seed
Function: string-unfold-right stop? seed->char make-seed first-seed base-string
Function: string-unfold-right stop? seed->char make-seed first-seed base-string make-final

This is a fundamental constructor for strings. The arguments are like the ones of string-unfold. The difference from string-unfold is that this function builds the string from right to left; more precisely, the following (simple, inefficient) definitions hold:

;; iterative
(define (string-unfold-right
           stop? seed->char make-seed
           first-seed base-string make-final)
  (let lp ((seed    first-seed)
           (result  base))
    (if (stop? seed)
        (string-append (make-final seed) result)
      (loop (make-seed seed)
            (string-append (string (seed->char seed))
                           result)))))

;; recursive
(define (string-unfold-right
           stop? seed->char make-seed
           first-seed base-string make-final)
  (string-append
     (let loop ((seed first-seed))
       (if (stop? seed)
           (make-final seed)
         (string-append (loop (make-seed seed))
                        (string (seed->char seed)))))
     base))

Interested functional programmers may enjoy noting that string-fold and string-unfold-right are in some sense inverses. That is, given operations knull?, kar, kdr, kons, and knil satisfying:

(kons (kar x) (kdr x)) ⇒ x
(knull? knil) ⇒ #t

then:

(string-fold kons knil
             (string-unfold-right knull? kar kdr x))
⇒ x

and:

(string-unfold-right knull? kar kdr
                     (string-fold kons knil s))
⇒ s

The final string constructed does not share storage with either base-string or the value produced by make-final.


Next: , Previous: , Up: strings   [Index]