Monday, March 13, 2006
RACTER, generative text and Lisp. Oh my! Part II
I found programming in LispSchemeguile
to be quite frustrating—partly due to it being an
unfamiliar environment to program in and partly due to not fully
understanding the implications of my design decisions for the generative
text platform I'm developing.
Ideally, I wanted something like:
(define firstname #( "Alice" "Bob" "Carol" "Dave" ) ) (define lastname #( "Smith" "Jones" "Conner" ) ) (define heros ;; from scifi.scm from the other day #( "Tim Conway" "Big Bird" "The Fonz" "Wonder Woman" "Kareem Abdul Jabar" "Tom Selleck" "Lucille Ball" #((ref firstname) " " (ref lastname)) ) )
Where heros
will be one of “Tim Conway” or a generated
named like “Carol Jones.” And it was handling this “second level” of
references where I had difficulty (lots of unbound and/or undefined
references—God I can't stand dynamic languages). I spend hours trying to
figure out how to iterate through an array. I mean, in a C-like language it
would be something like:
for (i = 0 ; i < array.length ; i++) { if (typeof(array[i]) == string) result.append(array[i]); else if (typeof(array[i]) == array) result.append(expand_array(array[i])); }
But LispSchemeguile
is rather
lacking in the loop department; instead you're supposed to use tail
recursion or one of the mapping functions and LispSchemeguile
's support of arrays isn't quite as
dynamic as say, Perl, and well …
I found the experience rather trying.
But in the end the code turned out to be rather simple. Well, once I “fixed” the data structures being used:
(define firstname #( "Alice" "Bob" "Carol" "Dave" ) ) (define lastname #( "Smith" "Jones" "Conner" ) ) (define heros ;; from scifi.scm from the other day #( "Tim Conway" "Big Bird" "The Fonz" "Wonder Woman" "Kareem Abdul Jabar" "Tom Selleck" "Lucille Ball" ((ref firstname) " " (ref lastname)) ;; look! a list! Not an array! ) ) (define rndstate 0) (define refn array-ref) ;; return an element from an array ;; need this to get random numbers out of (random) (set! rndstate (seed->random-state (current-time))) (define (nref a) (car (array-dimensions a))) (define (ref a) (let ((result (refn a (random (nref a) rndstate)))) (cond ((string? result) result) ((list? result) (eval (cons 'string-append result) (interaction-environment) )) ) ) )
Now that this is working, adding additional functions and variables
should be straightforward. And today's example is a guile
version of my Quick-n-Dirty B-Movie Plot Generator.