; XEmacs: This file contains -*- Scheme -*- source code. ;;; Naming the baby ;;; John David Stone ;;; Department of Mathematics and Computer Science ;;; Grinnell College ;;; stone@cs.grinnell.edu ;;; created February 10, 2000 ;;; last revised February 10, 2000 ;;; When selecting given names for their future child, expectant parents ;;; often study long lists of names and their derivations and ;;; pronunciations, hoping to express and guide the child's development ;;; through his or her name. One limitation of the method of scanning a ;;; list, however, is that parents sometimes don't pay enough attention to ;;; how their chosen names will sound in combination with each other and ;;; with the surname. By the time Jim and Susan Doyle send out the cards ;;; announcing the birth of their daughter, Lindsey, it's too late to point ;;; out the homonymy with ``linseed oil.'' This is depressing all around. ;;; The purpose of this program is to define a procedure FULL-NAMES that ;;; helps parents avoid such unintended clashes by listing, in full, every ;;; possible first-name/surname and first-name/middle-name/surname ;;; combination that can be formed from the names that the parents are ;;; actively considering. For instance, let's suppose that the Doyles' ;;; list of possible first names included Emily, Lindsey, Carrie, and ;;; Sarah, and the possible middle names are Anne, Lynn, and Lucy. They ;;; invoke the FULL-NAMES procedure thus: ;;; (full-names (list "Emily" "Lindsey" "Carrie" "Sarah") ;;; (list "Anne" "Lynn" "Lucy") ;;; "Doyle") ;;; and it returns the list ;;; ("Emily Doyle" "Lindsey Doyle" "Carrie Doyle" "Sarah Doyle" ;;; "Emily Anne Doyle" "Emily Lynn Doyle" "Emily Lucy Doyle" ;;; "Lindsey Anne Doyle" "Lindsey Lynn Doyle" "Lindsey Lucy Doyle" ;;; "Carrie Anne Doyle" "Carrie Lynn Doyle" "Carrie Lucy Doyle" ;;; "Sarah Anne Doyle" "Sarah Lynn Doyle" "Sarah Lucy Doyle") ;;; -- that is, a list that has as its elements every possible combination ;;; of a first name and the surname, and also every possible combination ;;; of first name, middle name, and surname. ;;; As a warm-up, let's begin with a procedure that takes a list of ;;; forenames and a surname, and returns a list of all the forename/surname ;;; combinations. This is a straightforward recursion over the list of ;;; forenames. (define binomials (lambda (forenames surname) (if (null? forenames) null (cons (string-append (car forenames) spacer surname) (binomials (cdr forenames) surname))))) ;;; The ``spacer'' referred to in the definition of BINOMIALS is simply the ;;; string that separates the forename from the surname. It consists of ;;; one space character. (define spacer " ") ;;; We can invoke BINOMIALS with the list of first names and the surname to ;;; get the two-component (no middle name) name-strings. To get the ;;; three-component name-strings requires a somewhat different approach. ;;; The difficulty is to coordinate two different list recursions, one ;;; involving the list of first names and the other involving the list of ;;; middle names. These recursions have to work independently of each ;;; other if we are going to get all possible first-name/middle-name ;;; combinations. ;;; The solution is to write separate procedures for the separate list ;;; recursions, invoking one of the procedures repeatedly from inside the ;;; other. ;;; Let's begin with the list recursion over the middle names. For a given ;;; first name and a given last name, we can fill in each possible middle ;;; name and return a list of the resulting three-component name-strings. ;;; The FILL-IN-MIDDLE-NAMES procedure carries out this part of the work. ;;; For instance, if we give it the first name "Jason", the list ("Albert" ;;; "Conrad" "William") of middle names, and the last name "Morrissey", it ;;; should construct and return the list ;;; ("Jason Albert Morrissey" "Jason Conrad Morrissey" ;;; "Jason William Morrissey") ;;; Since we can use STRING-APPEND to combine any number of strings, we can ;;; assemble each three-component name-string with only one invocation of ;;; STRING-APPEND. (define fill-in-middle-names (lambda (first-name middle-names surname) (if (null? middle-names) null (cons (string-append first-name spacer (car middle-names) spacer surname) (fill-in-middle-names first-name (cdr middle-names) surname))))) ;;; The TRINOMIALS procedure invokes FILL-IN-MIDDLE-NAMES repeatedly, once ;;; for each of the first names that the parents provide, and collects the ;;; lists that FILL-IN-MIDDLE-NAMES returns into one long list (by invoking ;;; APPEND to concatenate each list to the recursive result). ;;; Again, we use list recursion to manage the process. When the list of ;;; first names is empty, no three-component name-strings can be ;;; constructed, so we return an empty list. When the list of first names ;;; is not empty, we split off the first element of that list, invoke ;;; FILL-IN-MIDDLE-NAMES to construct a list of all the three-component ;;; name-strings that begin with that first name, invoke TRINOMIALS ;;; recursively to construct a list of all the three-component name-strings ;;; that begin with other first names, and invoke APPEND to combine the two ;;; lists. (define trinomials (lambda (first-names middle-names surname) (if (null? first-names) null (append (fill-in-middle-names (car first-names) middle-names surname) (trinomials (cdr first-names) middle-names surname))))) ;;; Finally, the FULL-NAMES procedure invokes BINOMIALS and TRINOMIALS to ;;; get lists of the two-component and three-component name-strings and ;;; combines them into one long list. (define full-names (lambda (first-names middle-names surname) (append (binomials first-names surname) (trinomials first-names middle-names surname)))) ;;; Here are the results of my test runs. ;;; > (full-names (list "Emily" "Lindsey" "Carrie" "Sarah") ;;; (list "Anne" "Lynn" "Lucy") ;;; "Doyle") ;;; ("Emily Doyle" ;;; "Lindsey Doyle" ;;; "Carrie Doyle" ;;; "Sarah Doyle" ;;; "Emily Anne Doyle" ;;; "Emily Lynn Doyle" ;;; "Emily Lucy Doyle" ;;; "Lindsey Anne Doyle" ;;; "Lindsey Lynn Doyle" ;;; "Lindsey Lucy Doyle" ;;; "Carrie Anne Doyle" ;;; "Carrie Lynn Doyle" ;;; "Carrie Lucy Doyle" ;;; "Sarah Anne Doyle" ;;; "Sarah Lynn Doyle" ;;; "Sarah Lucy Doyle") ;;; > (full-names (list "Michael") ;;; (list "James" "Edward" "Sunbeam") ;;; "O'Flaherty") ;;; ("Michael O'Flaherty" ;;; "Michael James O'Flaherty" ;;; "Michael Edward O'Flaherty" ;;; "Michael Sunbeam O'Flaherty") ;;; > (full-names (list "Boris" "Fyodor" "Andrei") null "Ivanov") ;;; ("Boris Ivanov" "Fyodor Ivanov" "Andrei Ivanov") ;;; > (full-names null (list "Guillaume" "Antoine" "Anatole") "Duplessis") ;;; () ;;; > (full-names null null "Knoll") ;;; ()