Review of Character Processing:
Literal Strings: A string is a sequence of characters. The external form of a string is the characters enclosed in double-quotes:
"This is a string!"Special characters can be included in a string by escaping them with a back-slash:
"Type \"stop\" to quit."Zero-based Indexing: While much work with strings does not require access to individual characters within a string, some procedures reference positions in a string. In such cases, Scheme numbers character positions within strings starting at position 0. For example, consider the string:
"I am very excited by the Scheme programming language!!!"Scheme regards the first character (I) as being in position 0, followed by a blank or space character in position 1. The letters 'a' and 'm' follow in positions 2 and 3, respectively.
Some String Procedures: Some common string procedures are shown in the following table:
| Procedure | Sample Call | Result of Example | Comment |
|---|---|---|---|
| string? | (string? "sample string") | True (#t) | is argument a string? |
| string-length | (string-length "sample string") | 13 | number of characters in string |
| string-append | (string-append "Big" "Small") | "BigSmall" | concatenate two strings |
| substring | (substring "sample string" 3 10) | "ple str" | extract characters from first to before second designated position from string |
| string-ref | (string-ref "sample string" 4) | #\l | return character at given position |
| string->list | (string->list "example") | (#\e #\x #\a #\m #\p #\l #\e) | makes a list of the characters in a string |
| list->string | (list->string '(#\e #\x #\a #\m #\p #\l #\e)) | "example" | makes a string of the characters in a list |
| symbol->string | (symbol->string 'example) | "example" | change a given symbol to a string |
| string->symbol | (string->symbol "example") | example | convert a given string to a symbol |
Some Comparisons of Strings: Scheme also provides various predicates to compare two strings are equal:
| Procedure | Comment |
|---|---|
| string=? | Are two strings equal? |
| string | Does first string come first? |
| string>? | Does first string come after? |
| string<=? | Is first string equal the second or does the first come before the second? |
| string>=? | Are the strings equal or does the first come after the second? |
Scheme also provides string predicates which are case-insensitive:
| string-ci=? | Same as string=?, but ignoring case |
| string-ci | Same as string, but considering uppercase and lowercase letters to be equivalent |
| string-ci>? | Same as string>?, but ignoring case |
| string-ci<=? | Same as string<=?, but ignoring case |
| string-ci>=? | Same as string>=?, but ignoring case |
Additional Discussion: Reread sections 6.1-6.2 of the textbook.
Example: Consider the problem of counting the number of vowels within a string.
Approach 1: Convert the letters of the string to a list, and recursively count the vowels on the list. This might lead to the following code (which uses vowel? from earlier in this lab).
(define number-vowels
(lambda (str)
(number-vowels-kernel (string->list str))
)
)
(define number-vowels-kernel
(lambda (ls)
(cond ((null? ls) 0)
((vowel? (car ls)) (+ 1 (number-vowels-kernel (cdr ls))))
(else (number-vowels-kernel (cdr ls)))
)
)
)
(define number-vowels
(lambda (str)
(count-vowels-by-position str 0 0)
)
)
(define count-vowels-by-position
(lambda (str current-count current-position)
(cond ((= current-position (string-length str))
current-count )
((vowel? (string-ref str current-position))
(count-vowels-by-position str
(+ 1 current-count)
(+ 1 current-position)))
(else (count-vowels-by-position str current-count
(+ 1 current-position)))
)
)
)
Again, it's possible to put the kernel inside the husk. Here's how
number-vowels looks if a named let is used to
control the recursion:
(define number-vowels
(lambda (str)
(let ((len (string-length str)))
(let kernel ((current-count 0)
(current-position 0))
(if (= current-position len)
current-count
(kernel (if (vowel? (string-ref str current-position))
(+ current-count 1)
current-count)
(+ current-position 1)))))))
count-vowels-by-position, as a separate procedure, has
three parameters, it's a little surprising that the internal
kernel procedure in the latest version has only two. Which
one is missing? Why is it unnecessary to supply it?
This version also incorporates another improvement that is easy to make
once the kernel is inside the husk: The length of the string
str can be computed just once and stored in a local variable,
len, instead of being recomputed on each recursive call.
This document is available on the World Wide Web as
http://www.math.grin.edu/~walker/courses/151/lab-strings.html