markov generate the bible
This commit is contained in:
commit
45466ce4c4
|
@ -0,0 +1,13 @@
|
|||
.PHONY: run
|
||||
|
||||
run: kjv.txt markov.chicken.so markov.import.scm
|
||||
csi toplevel.scm
|
||||
|
||||
markov.chicken.scm: markov.impl.scm
|
||||
markov.chicken.so markov.import.scm: markov.chicken.scm
|
||||
csc -s -J markov.chicken.scm -O3
|
||||
|
||||
kjv.txt:
|
||||
csi -b kjv2delimited.scm
|
||||
clean:
|
||||
rm -f kjv.txt markov.chicken.so markov.import.scm
|
|
@ -0,0 +1,15 @@
|
|||
=============
|
||||
Markov Scheme
|
||||
=============
|
||||
|
||||
Markov chain text generation in R5RS.
|
||||
|
||||
The generator should run in any Scheme, but it runs much faster in
|
||||
Chicken.
|
||||
|
||||
Run ``make`` to compile the generator and enter a toplevel where you can
|
||||
use the markov generator to generate bible verses.
|
||||
|
||||
License: Apache-2.0. The SQLite database file is GPL-3.0-or-later.
|
||||
|
||||
Requires eggs ``srfi-69`` and ``sql-de-lite``.
|
Binary file not shown.
|
@ -0,0 +1,30 @@
|
|||
(import sql-de-lite)
|
||||
|
||||
(define (!= x y)
|
||||
(not (= x y)))
|
||||
|
||||
(with-output-to-file "kjv.txt"
|
||||
(lambda ()
|
||||
(call-with-database "bible-sqlite.db"
|
||||
(lambda (db)
|
||||
(let ((previous-book 0)
|
||||
(previous-chapter 0)
|
||||
(previous-verse 0))
|
||||
(query (for-each-row
|
||||
(lambda (row)
|
||||
(let ((book (car row))
|
||||
(chapter (cadr row))
|
||||
(verse (caddr row))
|
||||
(text (cadddr row)))
|
||||
(when (!= book previous-book)
|
||||
(display "startofbook\n"))
|
||||
(when (!= chapter previous-chapter)
|
||||
(display "startofchapter\n"))
|
||||
(when (!= verse previous-verse)
|
||||
(display "startofverse\n"))
|
||||
(set! previous-book book)
|
||||
(set! previous-chapter chapter)
|
||||
(set! previous-verse verse)
|
||||
(display text)
|
||||
(newline))))
|
||||
(sql db "select b,c,v,t from t_kjv")))))))
|
|
@ -0,0 +1,4 @@
|
|||
(module markov
|
||||
(make-table-from-file guess-sentence generate-next-word)
|
||||
(import scheme (chicken base) srfi-69)
|
||||
(include "markov.impl.scm"))
|
|
@ -0,0 +1,212 @@
|
|||
(define char-downcase*
|
||||
(lambda (ch)
|
||||
(if (char? ch)
|
||||
(char-downcase ch)
|
||||
ch)))
|
||||
|
||||
(define port->reader
|
||||
(lambda (port)
|
||||
(list (lambda () (read-char port))
|
||||
eof-object?)))
|
||||
|
||||
(define reader/pushback-buffer
|
||||
(lambda (read-function eof?)
|
||||
(let ((pushback-buffer '()))
|
||||
(letrec ((reader
|
||||
(lambda ()
|
||||
(if (null? pushback-buffer)
|
||||
(read-function)
|
||||
(let ((ch (car pushback-buffer)))
|
||||
(set! pushback-buffer (cdr pushback-buffer))
|
||||
ch))))
|
||||
(pushback
|
||||
(lambda (ch)
|
||||
(if (not (eof? ch))
|
||||
(set! pushback-buffer (cons ch pushback-buffer))))))
|
||||
(list pushback reader eof?)))))
|
||||
|
||||
(define reader-words
|
||||
(lambda (part-of-a-word? pushback reader eof?)
|
||||
(letrec ((delimiter?
|
||||
(lambda (ch)
|
||||
(or (eof? ch)
|
||||
(not (part-of-a-word? ch))
|
||||
(eqv? ch #\space))))
|
||||
(read-until-delimiter
|
||||
(lambda (lst)
|
||||
(let ((ch (reader)))
|
||||
(if (delimiter? ch)
|
||||
(begin
|
||||
(pushback ch)
|
||||
(list->string (reverse lst)))
|
||||
(read-until-delimiter (cons ch lst))))))
|
||||
(reader-wrapper
|
||||
(lambda ()
|
||||
(let ((ch (reader)))
|
||||
(cond
|
||||
((not (char? ch)) #f)
|
||||
((char-whitespace? ch) (reader-wrapper))
|
||||
(else (read-until-delimiter (list ch))))))))
|
||||
(list reader-wrapper))))
|
||||
|
||||
(define port->reader-words
|
||||
(lambda (port)
|
||||
(apply reader-words
|
||||
char-alphabetic?
|
||||
(apply reader/pushback-buffer
|
||||
(port->reader port)))))
|
||||
|
||||
(define reader-pairs
|
||||
(lambda (reader)
|
||||
(let ((previous-word #f))
|
||||
(lambda ()
|
||||
(if (not previous-word)
|
||||
(set! previous-word (reader)))
|
||||
(if previous-word
|
||||
(let ((next-word (reader))
|
||||
(previous-word* previous-word))
|
||||
(if next-word
|
||||
(begin
|
||||
(set! previous-word next-word)
|
||||
(cons previous-word* next-word))
|
||||
#f))
|
||||
#f)))))
|
||||
|
||||
(define port->reader-of-pairs
|
||||
(lambda (port)
|
||||
(apply reader-pairs (port->reader-words port))))
|
||||
|
||||
(define dump-pairs
|
||||
(lambda (file)
|
||||
(let ((port (open-input-file file)))
|
||||
(let ((reader (port->reader-of-pairs port)))
|
||||
(letrec ((loop
|
||||
(lambda ()
|
||||
(let ((pair (reader)))
|
||||
(if pair
|
||||
(begin
|
||||
(display pair)
|
||||
(loop)))))))
|
||||
(loop)
|
||||
(newline))))))
|
||||
|
||||
(define collect-occurences
|
||||
(lambda (file)
|
||||
(let ((port (open-input-file file))
|
||||
(table (make-hash-table)))
|
||||
(let ((reader (port->reader-of-pairs port)))
|
||||
(letrec ((handle
|
||||
(lambda (first next)
|
||||
(hash-table-update!
|
||||
table
|
||||
first
|
||||
(lambda (next-table)
|
||||
(hash-table-update!/default
|
||||
next-table
|
||||
next
|
||||
(lambda (count) (+ count 1))
|
||||
1)
|
||||
next-table)
|
||||
(lambda () (make-hash-table)))
|
||||
(looper)))
|
||||
(looper
|
||||
(lambda ()
|
||||
(let ((pair (reader)))
|
||||
(if pair
|
||||
(handle (car pair) (cdr pair)))))))
|
||||
(looper)
|
||||
(close-input-port port)
|
||||
table)))))
|
||||
|
||||
(define display-occurences
|
||||
(lambda (table)
|
||||
(hash-table-walk table
|
||||
(lambda (first next-table)
|
||||
(hash-table-walk next-table
|
||||
(lambda (next count)
|
||||
(display (list first next count))))))
|
||||
(newline)))
|
||||
|
||||
(define occurence-table->probabilities
|
||||
(lambda (next-table)
|
||||
(let ((total (hash-table-fold next-table
|
||||
(lambda (_ val total)
|
||||
(+ val total))
|
||||
0)))
|
||||
(let ((acc
|
||||
(hash-table-fold next-table
|
||||
(lambda (key val acc)
|
||||
(let ((summed-probability (car acc))
|
||||
(alist (cdr acc)))
|
||||
(let ((summed-probability
|
||||
(+ summed-probability
|
||||
(/ val total))))
|
||||
(cons summed-probability
|
||||
(cons (cons key summed-probability)
|
||||
alist)))))
|
||||
(cons 0 '()))))
|
||||
(reverse (cdr acc))))))
|
||||
|
||||
(define occurences->probabilities
|
||||
(lambda (table)
|
||||
(let ((probability-table
|
||||
(hash-table-fold table
|
||||
(lambda (name next-table new-table)
|
||||
(hash-table-set! new-table
|
||||
name
|
||||
(occurence-table->probabilities
|
||||
next-table))
|
||||
new-table)
|
||||
(make-hash-table))))
|
||||
probability-table)))
|
||||
|
||||
(define dump-probabilities
|
||||
(lambda (table)
|
||||
(hash-table-walk table
|
||||
(lambda (name probs)
|
||||
(display (list name probs))))
|
||||
(newline)))
|
||||
|
||||
;;; Lehmer random number generator
|
||||
(define random
|
||||
(let ((m 65537)
|
||||
(a 75)
|
||||
(previous 10000))
|
||||
(lambda ()
|
||||
(set! previous (remainder (* a previous)
|
||||
m))
|
||||
(/ previous (- m 1)))))
|
||||
|
||||
(define probability-list->guess
|
||||
(lambda (lst normalized-random-number)
|
||||
(if (null? lst)
|
||||
(error (list "bad number or list" lst normalized-random-number))
|
||||
(let ((name (caar lst))
|
||||
(sum-prob (cdar lst)))
|
||||
(if (< sum-prob normalized-random-number)
|
||||
(probability-list->guess (cdr lst)
|
||||
normalized-random-number)
|
||||
name)))))
|
||||
|
||||
(define generate-next-word
|
||||
(lambda (probabilities previous-word)
|
||||
(probability-list->guess (hash-table-ref probabilities
|
||||
previous-word)
|
||||
(random))))
|
||||
|
||||
(define make-table-from-file
|
||||
(lambda (filename)
|
||||
(occurences->probabilities
|
||||
(collect-occurences filename))))
|
||||
|
||||
(define guess-sentence
|
||||
(lambda (start-word table number)
|
||||
(letrec ((collector
|
||||
(lambda (start-word number collected)
|
||||
(if (<= number 0)
|
||||
(reverse collected)
|
||||
(let ((name (generate-next-word table start-word)))
|
||||
(collector name (- number 1) (cons name
|
||||
collected)))))))
|
||||
(collector start-word number (cons start-word '())))))
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
(load "markov.chicken.so")
|
||||
(import markov)
|
||||
|
||||
(display "Generating table (this may take a while)\n" (current-error-port))
|
||||
(define table (make-table-from-file "kjv.txt"))
|
||||
(display "Done generating.\n")
|
||||
|
||||
(define (generate-until-word* current collected limits)
|
||||
(if (member current limits)
|
||||
(reverse collected)
|
||||
(generate-until-word* (generate-next-word table current)
|
||||
(cons current collected)
|
||||
limits)))
|
||||
|
||||
(define (generate-until-word limits)
|
||||
(generate-until-word* (generate-next-word table (car limits))
|
||||
'()
|
||||
limits))
|
||||
|
||||
(define (make-a-bible-verse)
|
||||
(generate-until-word '("startofverse" "startofchapter" "startofbook")))
|
||||
|
||||
(define (make-a-book-of-the-bible)
|
||||
(generate-until-word '("startofbook")))
|
||||
|
||||
(display "Try (make-a-bible-verse) and (make-a-book-of-the-bible).\n")
|
Loading…
Reference in New Issue