Discussion:
string-unfold demo
Matt Wette
2017-08-26 17:38:14 UTC
Permalink
Just for kicks, to learn string-unfold, I made an ugly version of string-append:

(define (ugly-string-append . str-l)

;; p: seed |-> #t|#f predicate to indicate stop
(define (p seed) (null? seed))

;; f: seed |-> char output function
(define (f seed) (string-ref (cddar seed) (caar seed)))

;; g: seed |-> seed transition function
(define (g seed) (let* ((head (car seed)) (tail (cdr seed))
(ix (car head)) (ln (cadr head)) (st (cddr head)))
(if (eq? (1+ ix) ln) tail
(cons (cons* (1+ ix) ln st) tail))))

;; s: seed = ((ix1 ln1 . st1) (ix2 ln2 . st2) ...)
;; where ix is curr index, ln is string-length, and st is string
(define s (map (lambda (s) (cons* 0 (string-length s) s)) str-l))

(string-unfold p f g s))

(ugly-string-append "abc" "def") => "abcdef"
Alex Vong
2017-08-28 08:56:50 UTC
Permalink
Hello Guilers,

Since I find this exercise interesting, I come up with another demo. The
trick is to think of string as a list of characters (like in Haskell)
and to use the fact that append can be written as an unfold.

Let's begin:

1. Use SRFI-1 and SRFI-26

(use-modules (srfi srfi-1)
(srfi srfi-26))

2. Implement append using unfold

(define (append a b)
(unfold null?
car
cdr
a
(const b)))

Test it:

(append '(1 2 3) '(4 5 6))
=> (1 2 3 4 5 6)

3. Extend append to allow any number of arguments

(define append
(lambda args
(define (%append a b)
(unfold null?
car
cdr
a
(const b)))
(fold-right %append '() args)))

Test it:

(append '(1 2 3) '(4 5 6) '(7 8 9))
=> (1 2 3 4 5 6 7 8 9)

4. "Port" the procedure to "string"-land

First, notice 'unfold' takes a TAIL-GEN procedure to generate the tail,
while string-unfold takes a BASE string, not a procedure. So, let's
replace '(const b)' with 'b'. Next, replace 'null?' with 'string-null?',
'car' with '(cut string-ref <> 0)' and 'cdr' with
'(cut string-drop <> 1)'

(define (string-append a b)
(string-unfold string-null?
(cut string-ref <> 0)
(cut string-drop <> 1)
a
b))

Test it:

(string-append "123" "456")
=> "456123"

Oops, we got the a and b reversed.

5. Reverse a and b

(define (string-append a b)
(string-unfold string-null?
(cut string-ref <> 0)
(cut string-drop <> 1)
b
a))

Test it:

(string-append "123" "456")
=> "123456"

Now it works.

6. Extend again to allow any number of arguments

(define string-append
(lambda args
(define (%string-append a b)
(string-unfold string-null?
(cut string-ref <> 0)
(cut string-drop <> 1)
b
a))
(fold-right %string-append "" args)))

Test it:

(string-append "123" "456" "789")
=> "123456789"

DONE!

Cheers,
Alex
Post by Matt Wette
(define (ugly-string-append . str-l)
;; p: seed |-> #t|#f predicate to indicate stop
(define (p seed) (null? seed))
;; f: seed |-> char output function
(define (f seed) (string-ref (cddar seed) (caar seed)))
;; g: seed |-> seed transition function
(define (g seed) (let* ((head (car seed)) (tail (cdr seed))
(ix (car head)) (ln (cadr head)) (st (cddr head)))
(if (eq? (1+ ix) ln) tail
(cons (cons* (1+ ix) ln st) tail))))
;; s: seed = ((ix1 ln1 . st1) (ix2 ln2 . st2) ...)
;; where ix is curr index, ln is string-length, and st is string
(define s (map (lambda (s) (cons* 0 (string-length s) s)) str-l))
(string-unfold p f g s))
(ugly-string-append "abc" "def") => "abcdef"
Loading...