>>>>> "B" == Bill:
B> I think the bytevector? problem is that you have two versions of
B> s7 running
Right, there was a previous one installed via the package-system here.
Got rid of that and things work.
Here's a (slightly modded) version of CM_patterns.scm which can be
loaded in any running snd (built from current sources):
;;; **********************************************************************
;;; Copyright (c) 2008, 2009 Rick Taube.
;;; This program is free software; you can redistribute it and/or modify
;;; it under the terms of the Lisp Lesser Gnu Public License. The text of
;;; this agreement is available at http://www.cliki.net/LLGPL
;;; **********************************************************************
;;; [2015-06-11, AV]: adapting to stand-alone loading in a running 's7' (i.e.
snd), start
;;; snd, then load this file
(require r7rs.scm)
;; some functions collect from various places (s7.scm, utilities.scm)
(define-macro (with-optkeys spec . body)
((lambda (user rawspec body)
(define (string->keyword str) (symbol->keyword (string->symbol str)))
(define (key-parse-clause info mode args argn user)
;; return a cond clause that parses one keyword. info for each
;; var is: (<got> <var> <val>)
(let* ((got (car info))
(var (cadr info))
(key (string->keyword (symbol->string var))))
`((eq? (car ,args) ,key )
(if ,got (error "duplicate keyword: ~S" , key))
(set! ,var (if (null? (cdr ,args))
(error "missing value for keyword: ~S"
, user)
(cadr ,args)))
(set! ,got #t) ; mark that we have a value for this param
(set! ,mode #t) ; mark that we are now parsing keywords
(set! ,argn (+ ,argn 1))
(set! ,args (cddr ,args)))))
(define (pos-parse-clause info mode args argn I)
;; return a cond clause that parses one positional. info for
;; each var is: (<got> <var> <val>)
(let ((got (car info))
(var (cadr info)))
`((= ,argn ,I)
(set! ,var (car ,args))
(set! ,got #t) ; mark that we have a value for this param
(set! ,argn (+ ,argn 1))
(set! ,args (cdr ,args)))))
(let* ((otherkeys? (member '&allow-other-keys rawspec))
;; remove &allow-other-keys from spec
(spec (if otherkeys? (reverse (cdr (reverse rawspec))) rawspec))
(data (map (lambda (v)
;; for each optkey variable v return a list
;; (<got> <var> <val>) where the <got>
;; variable indicates that <var> has been
;; set, <var> is the optkey variable itself
;; and <val> is its default value
(if (pair? v)
(cons (gensym (symbol->string (car v))) v)
(list (gensym (symbol->string v)) v #f)))
spec))
(args (gensym "args")) ; holds arg data as its parsed
(argn (gensym "argn"))
(SIZE (length data))
(mode (gensym "keyp")) ; true if parsing keywords
;; keyc are cond clauses that parse valid keyword
(keyc (map (lambda (d) (key-parse-clause d mode args argn user))
data))
(posc (let lup ((tail data) (I 0))
(if (null? tail) (list)
(cons (pos-parse-clause (car tail) mode args argn I)
(lup (cdr tail) (+ I 1))))))
(bindings (map cdr data)) ; optkey variable bindings
)
(if otherkeys?
(set! bindings (cons '(&allow-other-keys (list)) bindings)))
`(let* ,bindings ; bind all the optkey variables with default values
;; bind status and parsing vars
(let ,(append (map (lambda (i) (list (car i) #f)) data)
`((,args ,user)
(,argn 0)
(,mode #f)))
;; iterate arglist and set opt/key values
(do ()
((null? ,args) #f)
(cond
;; add valid keyword clauses first
,@ keyc
;; a keyword in (car args) is now either added to
;; &allow-other-keys or an error
, (if otherkeys?
`((keyword? (car ,args))
(if (not (pair? (cdr ,args)))
(error "missing value for keyword ~S" (car ,args)))
(set! &allow-other-keys (append &allow-other-keys
(list (car ,args)
(cadr ,args))))
(set! ,mode #t) ; parsing keys now...
(set! ,args (cddr ,args)) )
`((keyword? (car ,args)) ;(and ,mode (keyword? (car
,args)))
(error "invalid keyword: ~S" (car ,args)) )
)
;; positional clauses illegal if keywords have happened
(,mode (error "positional after keywords: ~S" (car ,args)))
;; too many value specified
((not (< ,argn ,SIZE)) (error "too many args: ~S" , args))
;; add the valid positional clauses
,@ posc
))
,@ body))
))
(car spec)
(cdr spec)
body
))
;;; based on define-record-type from snd/r7rs.scm. Works with srfi-17?
(define-macro (define-record typename . fields)
`(define-record-type ,typename
(,(symbol (format #f "make-~A" typename)) ,@fields)
,(symbol (format #f "~A?" typename))
,@(map (lambda (x) `(,x
,(symbol (format #f "~A-~A" typename x))
,(symbol (format #f "~A-~A-set!" typename x))))
fields)))
;; grabbed from s7.scm:
(define (string->keyword s)
(make-keyword s))
(define (logtest a b)
(not (zero? (logand a b))))
(define sort sort!)
;; grabbed from utilities.scm:
(define (list-index p l)
(do ((tail l (cdr tail))
(i 0 (+ i 1))
(f #f))
((or f (null? tail ))
f)
(if ( p (car tail)) (set! f i))))
;; (autoload 'with-optkeys "scm/utilities.scm") ; + quite some others
;; (autoload 'string->keyword "scm/s7.scm")
;; scm/toolboox.scm:
(define (decimals value places)
(let ((n (expt 10.0 places)))
(if (list? value)
(map (lambda (v) (/ (round (* v n)) n)) value)
(/ (round (* value n)) n))))
;;;
;;; patterns using structs instead of classes.
;;; requires with-optkeys, arithmetic-test, list-set, tb:rani tb:ranf
;;;
;;;
;;; [AV, 2015-06-11]: various ffi_ random functions are substituded
;;; with schemes standard random
(define-constant +constant-data+ (ash 1 0)) ; avoid hair when possible
(define-constant +default-period+ (ash 1 1)) ; no period specified
(define-constant +constant-weights+ (ash 1 2)) ; avoid random index recalc
(define-constant +count-periods+ (ash 1 3)) ; period counts subperiods
(define-constant +count-values+ (ash 1 4)) ; period counts values
(define-constant +depth-first+ (ash 1 5)) ; pattern moves on eop
(define-constant +breadth-first+ (ash 1 6)) ; pattern moves each time
(define-constant +nad+ '#:nad) ; "not a datum" marker
(define-constant +eop+ '#:eop) ; "end of period" marker
(define-constant +eod+ '#:eod) ; "end of data" marker
;;; the period struct holds information for period calculation. count
;;; is number of reads remaining in current period. when count=0 the
;;; period is reinitialized. length is maximum count of the period,
;;; either a number or #t if dynamic length. if stream is not #f a new
;;; length will be read from it each time the period is initialized.
;;; omit is the number of times this stream is skipped in its parent's
;;; pattern, if dynamic. Reps keeps track of the number of
;;; periods. Max is the max number of periods allowed, after which the
;;; pattern always returns +eod+
(define-record period count length stream default omit reps )
(define (pperiod obj )
(list 'period
(period-count obj) (period-length obj) (period-stream obj)
(period-default obj) (period-omit obj) (period-reps obj)
))
(define-record pattern flags data length datum period value state
repeat returning counting traversing next mapr cache)
(define (ppattern obj )
(list 'pattern
#:flags (pattern-flags obj)
#:data (pattern-data obj)
#:length (pattern-length obj)
#:datum (pattern-datum obj)
#:period (pperiod (pattern-period obj))
#:value (pattern-value obj)
#:state (pattern-state obj)
#:repeat (pattern-repeat obj)
#:returning (pattern-returning obj)
#:cache (pattern-cache obj)
))
(define (%alloc-pattern)
;; flags data length datum period value state limit returning counting
traversing next mapr cache
(make-pattern 0 (list) #f +nad+ #f +nad+ +nad+ most-positive-fixnum #f
#:periods #:depth-first
#f #f #f))
(define (initialize-pattern obj data for rep flags len dper getr mapr)
(pattern-data-set! obj data)
(pattern-length-set! obj len)
(pattern-mapr-set! obj mapr)
(pattern-next-set! obj getr)
;; map data to see if it is constant data or has subpatterns
(let ((con? #t))
(map-pattern-data (lambda (x) (if (pattern? x) (set! con? #f)))
obj)
(if con? (set! flags (logior flags +constant-data+))))
;; parse counting option
(let ((counting (pattern-counting obj)))
(case counting
((#:periods )
(set! flags (logior flags +count-periods+)))
((#:values )
(set! flags (logior flags +count-values+)))
(else
(error "illegal counting value ~S" counting))))
;; parse traversing option
(let ((traversing (pattern-traversing obj)))
(case traversing
((#:depth-first )
(set! flags (logior flags +depth-first+)))
((#:breadth-first )
(set! flags (logior flags +breadth-first+)))
(else
(error "illegal traversing value ~S" traversing))))
;; if constant data and counting subperiods, switch to counting
;; values instead since its the same thing and we can avoid
;; resetting subperiods if period length is nevertheless expressed
;; dynamically.
(cond ((logtest flags +count-values+)
(set! flags (logand flags (lognot +count-periods+))))
(else
(if (logtest flags +constant-data+)
(set! flags (logior
(logand
flags (lognot +count-periods+))
+count-values+))
(set! flags (logior flags +count-periods+)))))
(pattern-repeat-set! obj (if (and (number? rep) (> rep 0))
rep most-positive-fixnum))
(let ((per (or for dper)))
;; period not specified so mark that we are using default period
(when (not for)
(set! flags (logior flags +default-period+)))
(pattern-period-set! obj
(if (or (number? per)
(eqv? per #t))
;; count len src dper omit reps
(make-period 0 per #f dper 0 0)
;; count len src dper omit reps
(make-period 0 #f per dper 0 0))))
(pattern-flags-set! obj flags)
(values))
;;;
;;; Predicates for testing end-of-period and end-of-data.
;;;
(define (eop? x)
(if (pattern? x)
(eop? (pattern-state x))
(eqv? x +eop+)))
(define (eod? x)
(if (pattern? x)
(eod? (pattern-value x))
(eqv? x +eod+)))
;;;
;;; next returns the next value read from the object. this around
;;; method implements the basic behavior of patterns. it first checks
;;; the stream's period length and calls reset-period if at end. if
;;; the next period length is 0 it immediately returns +nad+, which
;;; causes a superior stream (if any) to skip over the current stream
;;; as it increments its pattern. otherwise, the method then
;;; increments the streams pattern until it yields a datum that is not
;;; +nad+ and that call-next-method does not return +nad+ from. if the
;;; stream's data is known to contain only constant values, ie no
;;; substreams, the testing loop is skipped. once call-next-method
;;; returns a value (not +nad+), the period and pattern of the stream
;;; are incremented according to their mode. for period incrementing,
;;; +count-periods+ increments the period count only on +eop+, and
;;; +count-values+ increments the period count every time. for pattern
;;; incrementing, +depth-first+ increments the pattern only on +eop+,
;;; and +breadth-first+ increments the pattern every time.
;;;
(define (next obj . args)
(let ((num (if (null? args) #f (car args))))
(if num
(if (number? num )
(let ((l (list #f)))
(do ((i 0 (+ 1 i))
(e l (cdr e)))
((>= i num)
(cdr l))
(set-cdr! e (list (next-1 obj)))))
(if (pattern? obj)
(let ((l (list #f)))
(do ((n (next-1 obj) )
(e l (cdr e))
(f #f))
((or (eqv? n +eod+) f)
(cdr l))
(set-cdr! e (list n))
(if (eop? obj)
(set! f #t)
(set! n (next-1 obj)))))
(list obj)))
(next-1 obj))))
(define (next-1 obj)
(cond ((pattern? obj)
(let ((period (pattern-period obj))
(nomore #f))
;; reset period, return
(when (= (period-count period) 0)
(when (>= (period-reps period)
(pattern-repeat obj))
(pattern-value-set! obj +eod+)
(pattern-state-set! obj +eop+)
(set! nomore +eod+))
(when (and (not nomore)
(= (reset-period obj) 0))
(set! nomore +nad+)
(pattern-value-set! obj +nad+)
(pattern-state-set! obj +eop+)))
(if nomore
nomore
(let ((flags (pattern-flags obj))
(retfn (pattern-returning obj))
(value #f)
(state #f))
;; increment datum until not +nad+
(if (logtest flags +constant-data+)
(begin
(pattern-datum-set! obj (next-in-pattern obj))
(set! value (next-1 (pattern-datum obj)))
(set! state +eop+)
;;(print (list #:consant!))
)
(do ((dyn? (and (logtest flags +count-periods+)
(eqv? (period-length period) #t)))
(stop #f))
(stop #f)
;; increment over 0 length substreams
(do ()
((not (eqv? (pattern-datum obj) +nad+)) #f)
(pattern-datum-set! obj
(if dyn?
(skip-datum? (next-in-pattern
obj))
(next-in-pattern obj))))
(set! value (next-1 (pattern-datum obj)))
(if (pattern? (pattern-datum obj))
(set! state (pattern-state (pattern-datum obj)))
(set! state +eop+))
;; increment over +nad+ values returned by obj.
(if (eqv? value +nad+)
(pattern-datum-set! obj value)
(set! stop #t ))) )
;; increment period and pattern as appropriate.
(cond ((eqv? state +eop+)
;;(print (list #:state-eop!))
(period-count-set! period (- (period-count period) 1))
(pattern-datum-set! obj +nad+)
(set! state #f))
(else
(if (logtest flags +breadth-first+)
(pattern-datum-set! obj +nad+))
(if (logtest flags +count-values+)
(period-count-set! period
(- (period-count period) 1)))))
;;(print (list #:period-count (period-count period)))
(if (= (period-count period) 0)
(begin (set! state +eop+)
(period-reps-set! period
(+ 1 (period-reps period))))
(set! state state))
(if retfn
(set! value ( retfn value)));; thunk
(pattern-state-set! obj state)
(pattern-value-set! obj value)
value))))
((procedure? obj)
(obj ) )
(else
obj)))
(define (next-in-pattern obj)
( (pattern-next obj) obj)
)
(define (map-pattern-data fn obj)
( (pattern-mapr obj) fn obj)
)
;;;
;;; skip-datum? returns +nad+ if the current stream should be skipped
;;; in the pattern. this only happens if we have dynamic periodicity
;;; and the datum had a 0 length period when it was encountered by
;;; reset-period.
;;;
(define (skip-datum? obj)
(if (not (pattern? obj))
obj
(let ((period (pattern-period obj)))
(if (> (period-omit period) 0)
(begin (period-omit-set! period
(- (period-omit period) 1))
+nad+)
obj))))
;;;
;;; reset-period sets and returns the length of the next
;;; period. period length of constant datum is always 1.
;;;
(define (reset-period obj)
(if (not (pattern? obj)) 1
(let ((period (pattern-period obj))
(dyn #f)
(len #f))
;; if period is supplied as a stream get next length via item
(when (period-stream period)
(period-length-set! period
(next-1 (period-stream period))))
(set! dyn (eqv? (period-length period) #t))
(set! len
(if dyn
(period-default period)
(period-length period)))
;; if we have dynamic period length we adjust next period
;; length for the number of 0 subperiods that this period will
;; encounter. in order for this to work, all substream
;; periods must be reset now, at the same that the super
;; stream is reset. we can only do this if we know that all
;; subperiods are currently at end of period, ie if we are
;; counting by subperiods. if so, then by definition all the
;; substreams must be at end-of-period or we couldn't have
;; gotton here in the first place. after resetting substream
;; period lengths we decrement our current stream's period
;; length by the number of zero periods found.
(when (and dyn
(logtest (pattern-flags obj) +count-periods+))
(let ((zeros 0))
(map-pattern-data
(lambda (x)
(when (= (reset-period x) 0)
(let ((p (pattern-period x)))
(period-omit-set! p
(+ (period-omit p)
1)))
(set! zeros (+ zeros 1))
))
obj)
(when (> zeros 0)
(set! len (max (- len zeros) 0)))))
(period-count-set! period len)
len)))
;;;
;;; pattern implementations.
;;;
;;; cycle continously loops over its data. the data are held in a list
;;; of the form: (data . data). successive elements are popped from
;;; the cdr and when the cdr is null it's reset to the car.
;;;
(define (make-cycle data . args)
(unless (pair? data) (set! data (list data)))
(with-optkeys (args for limit)
(let ((obj (%alloc-pattern))
(flags 0)
(len (length data)))
(initialize-pattern obj (cons data data) for limit
flags len len next-in-cycle
(lambda (fn obj)
(for-each fn (car (pattern-data obj)))))
obj)))
(define (next-in-cycle obj)
(let ((data (pattern-data obj)))
(if (null? (cdr data))
(set-cdr! data (car data)))
(let ((x (cadr data)))
(set-cdr! data (cddr data))
x)))
; (define aaa (make-cycle (list 1 2 3)))
; (next aaa #t)
; (define aaa (make-cycle (list 1 2 3) :for 2))
; (next aaa #t)
; (define aaa (make-cycle (list 1 2 3) :for (make-cycle (list 3 2 1))))
; (next aaa #t)
; (define aaa (make-cycle (list 1 2 3) :limit 2))
; (next aaa #t)
; (define aaa (make-cycle (list (make-cycle (list 'a 'b) ) (make-cycle (list 1
2) ))))
; (next aaa #t)
; (define aaa (make-cycle (list 1 (make-cycle (list 'a 'b)))))
; (next aaa #t)
; (define aaa (make-cycle (list 1 (make-cycle (list 'a 'b) :for (make-cycle
(list 3 2 1 0))))))
; (next aaa #t)
;;;
;;; palindrome
;;;
(define-record-type palin
(make-palin pos len inc mode elide)
palin?
(pos palin-pos palin-pos-set!)
(len palin-len palin-len-set!)
(inc palin-inc palin-inc-set!)
(mode palin-mode palin-mode-set!)
(elide palin-elide palin-elide-set!))
(define (ppalin obj port)
(list 'palin
(palin-pos obj) (palin-len obj) (palin-inc obj)
(palin-mode obj) (palin-elide obj)))
(define (make-palindrome data . args)
(unless (pair? data) (set! data (list data)))
(with-optkeys (args for limit elide)
(let ((obj (%alloc-pattern))
(flags 0)
(len (length data)))
(initialize-pattern obj data for limit
flags len (* len 2) next-in-palindrome
(lambda (fn obj)
(for-each fn (pattern-data obj))))
;; pattern cache holds palin structure
(pattern-cache-set! obj (make-palin -2 (length data) #f #f
elide))
obj)))
(define (next-in-palindrome obj)
(let* ((cache (pattern-cache obj))
(pos (palin-pos cache)))
(cond ((< pos 0 )
;; starting new up-and-back cycle
(let ((m (next-1 (palin-elide cache)))
(l (palin-len cache))
(i (= pos -2)))
(palin-mode-set! cache m)
(palin-inc-set! cache 1)
;; see if we skip repeat of first element
(if (or (eqv? m #t) (and (pair? m) (eqv? (car m) #t)))
;; -2 marks very first call, dont skip inital element
(if i (set! pos 0) (set! pos 1))
(set! pos 0))
(if (logtest (pattern-flags obj) +default-period+)
(let* ((p (pattern-period obj))
(c (* l 2)))
(cond ((eqv? m #f)
(period-count-set! p c))
((eqv? m #t)
(period-count-set! p (if i (- c 2) (- c 3))))
((equal? m '(#f #t))
(period-count-set! p (- c 1)))
((equal? m '(#t #f))
(period-count-set! p (if i (- c 1) (- c 2))))
(else (period-count-set! p c)))
))
))
((= pos (palin-len cache))
;; reversing direction
(palin-inc-set! cache -1)
(let ((m (palin-mode cache)))
;; test if we skip repeat of last element
(if (or (eqv? m #t) (and (pair? m) (pair? (cdr m))
(eqv? (cadr m) #t)))
(set! pos (- pos 2))
(set! pos (- pos 1))))
))
(palin-pos-set! cache (+ pos (palin-inc cache)))
(list-ref (pattern-data obj) pos)))
; (define aaa (make-palindrome '(a b c d) ))
; (next aaa #t)
; (define aaa (make-palindrome '(a b c d) :elide #t))
; (next aaa #t)
; (define aaa (make-palindrome '(a b c d) :elide '(#f #t)))
; (next aaa #t)
; (define aaa (make-palindrome '(a b c d) :elide '(#t #f)))
; (next aaa #t)
; (define aaa (make-palindrome '(a b c d) :for 3))
; (next aaa #t)
;;;
;;; line sticks on the last element.
;;;
(define (make-line data . args)
(unless (pair? data) (set! data (list data)))
(with-optkeys (args for limit)
(let ((obj (%alloc-pattern))
(flags 0)
(len (length data)))
(initialize-pattern obj data for limit flags
len len next-in-line
(lambda (fn obj)
(for-each fn (pattern-data obj))))
obj)))
(define (next-in-line obj)
(let ((line (pattern-data obj)))
(if (null? (cdr line))
(begin
(period-count-set! (pattern-period obj) 1)
(car line)
)
(let ((x (car line)))
(pattern-data-set! obj (cdr line))
x))))
;;; (define aaa (make-line '(a b c)))
;;; (next aaa #t)
;;; (define aaa (make-line (list 'a 'b (make-cycle '(1 2 3 4)))))
;;; (next aaa #t)
;;; (define aaa (make-line (list 'a 'b (make-cycle '(1 2 3 4) :for (lambda ()
(+ 1 (random 4)))))))
;;; (next aaa #t)
;;; aaa
;;;
;;; heap shuffles its elements each time through
;;;
(define (make-heap data . args)
;; copy data because heap destructively modifies it
(if (pair? data)
(set! data (append data (list)))
(set! data (list data)))
(with-optkeys (args for limit)
(let ((obj (%alloc-pattern))
(flags 0)
(len (length data)))
(initialize-pattern obj (list data) for limit
flags len len next-in-heap
(lambda (fn obj)
(for-each fn (car (pattern-data obj)))))
obj)))
(define (next-in-heap obj)
(let ((data (pattern-data obj)))
(when (null? (cdr data))
(let ((len (pattern-length obj))
(lis (car data)))
(do ((i 0 (+ i 1))
(j (random len) (random len))
;; (j (ffi_ranint len ) (ffi_ranint len))
(v #f))
((= i len)
(set-cdr! data lis))
(set! v (list-ref lis i))
(list-set! lis i (list-ref lis j))
(list-set! lis j v))))
(let ((x (cadr data)))
(set-cdr! data (cddr data))
x)))
;; (define xxx '(1 2 3 4))
;; (define aaa (make-heap xxx))
;; (next aaa #t)
;; xxx
;; (define aaa (make-heap (list 1 2 3 (make-cycle '(a b c)) 4 5)))
;; (next aaa #t)
;;;
;;; rotation
;;;
(define (make-rotation data . args)
;; copy user's data (rotation side effects data)
(if (pair? data)
(set! data (append data (list)))
(set! data (list data)))
(with-optkeys (args for limit (rotate 0))
(let ((obj (%alloc-pattern))
(flags 0)
(len (length data)))
;; cdr of data initialized now so that rotations only happen
;; after the first cycle.
;; (initialize-pattern obj data args flags len dper getr mapr allow)
(initialize-pattern obj (cons data data) for limit
flags len len next-in-rotation
(lambda (fn obj)
(for-each fn (car (pattern-data obj)))))
;; pattern cache holds palin structure
(pattern-cache-set! obj rotate)
obj)))
(define (next-in-rotation obj)
(define (rotate-items items start step width end)
(do ((i start (+ i step)))
((not (< i end)) items)
(let ((a (list-ref items i))
(b (list-ref items (+ i width))))
(list-set! items i b)
(list-set! items (+ i width) a))))
(let ((data (pattern-data obj)))
(when (null? (cdr data))
(let ((l (car data))
(r (next-1 (pattern-cache obj))))
;; start step width end
(set-cdr! data
(if (pair? r)
(if (pair? (cdr r))
(if (pair? (cddr r))
(if (pair? (cdddr r))
(apply rotate-items l r)
(rotate-items l (car r)
(cadr r) (caddr r)
;; len - width
(- (pattern-length obj)
(caddr r))))
(rotate-items l (car r) (cadr r) 1
(- (pattern-length obj) 1)))
(rotate-items l (car r) 1 1
(- (pattern-length obj) 1)))
(rotate-items l r 1 1
(- (pattern-length obj) 1))))))
(let ((x (car (cdr data))))
(set-cdr! data (cddr data))
x)))
; (define aaa (make-rotation '(a b c d)))
; (next aaa #t)
; (define aaa (make-rotation '(a b c d) :rotations '(1 2)))
; (next aaa #t)
;;;
;;; weighting chooses items using weighted selection. its data are
;;; kept in a list of the form#: ((&rest choices) . last-choice).
;;;
;; (define-record random-item datum index weight min max count id minmax)
(define-record-type random-item
(make-random-item datum index weight min max count id minmax)
random-item?
(datum random-item-datum random-item-datum-set!)
(index random-item-index random-item-index-set!)
(weight random-item-weight random-item-weight-set!)
(min random-item-min random-item-min-set!)
(max random-item-max random-item-max-set!)
(count random-item-count random-item-count-set!)
(id random-item-id random-item-id-set!)
(minmax random-item-minmax random-item-minmax-set!))
(define (prandom-item obj )
(list 'random-item
#:datum (random-item-datum obj)
#:index (random-item-index obj)
#:weight (random-item-weight obj)
#:min (random-item-min obj)
#:max (random-item-max obj)
#:count (random-item-count obj)
#:id (random-item-id obj)
#:minmax (random-item-minmax obj)))
(define (make-weighting data . args)
(let* ((pool (canonicalize-weighting-data data))
(obj (%alloc-pattern))
(len (length pool))
(dper #f)
(const-weight #t)
(const-datums #t)
(num-patterns 0)
(flags 0))
(for-each (lambda (item)
(let ((min (random-item-min item))
(max (random-item-max item))
(wei (random-item-weight item))
(dat (random-item-datum item)))
(when (pattern? dat)
(set! const-datums #f)
(set! num-patterns (+ num-patterns 1)))
;; check the stream for constant weights. if true,
;; calculate the range now and set a flag so we dont
;; recalulate each period.
(unless (number? wei)
(set! const-weight #f))))
pool)
;; set the default period length of an all-subpattern weighting to
;; 1 otherwise to the number of elements. since a weighting
;; pattern establishes no particular order itself, setting the
;; period to 1 allows the number of elements in the current period
;; to reflect the sub patterns.
(set! dper (if (= num-patterns len) 1 len))
(if const-weight (set! flags (logior flags +constant-weights+)))
;; pool is ((&rest choices) . last-choice) no initial last
;; choice. a first choice for the stream could be implemented as a
;; last with min=1
(with-optkeys (args for limit)
(initialize-pattern obj (list pool) for limit
flags len dper next-in-weighting
(lambda (fn obj)
(for-each (lambda (i)
( fn (random-item-datum i)))
(car (pattern-data obj))))))
;; if we have constant weights calculate the range now as fixnums
(if const-weight (recalc-weightings obj #t))
obj))
(define (canonicalize-weighting-data data)
(define (%make-random-item w)
(let ((item #f)
(args (list)))
(cond ((pair? w)
(set! item (car w))
(set! args (cdr w)))
(else (set! item w)))
(with-optkeys (args (weight 1) (min 1) max)
(make-random-item item #f weight min max 0 #f #f))))
(map %make-random-item data))
;;; (canonicalize-weighting-data '(a b c))
;;; (canonicalize-weighting-data '(a (b 33) c))
;;; (canonicalize-weighting-data '(a (b :max 33) c))
(define (recalc-weightings obj fix?)
(let ((data (car (pattern-data obj)))
(range 0.0))
(do ((tail data (cdr tail)))
((null? tail) #f)
(set! range (+ range (next-1 (random-item-weight (car tail)))))
(random-item-index-set! (car tail) range))
(if fix?
(do ((tail data (cdr tail))
(index 0)
(total 0))
((null? tail)
(pattern-cache-set! obj total) )
(set! index (/ (random-item-index (car tail))
range))
(random-item-index-set! (car tail) index)
(set! total index))
(pattern-cache-set! obj range))))
(define (next-in-weighting obj)
;; pool is ((&rest choices) . last-item)
(let* ((pool (pattern-data obj))
(per (pattern-period obj))
(flags (pattern-flags obj))
(last (cdr pool)))
(unless (logtest flags +constant-weights+)
;; at beginning of new period?
(when (= (period-count per) (period-length per))
(recalc-weightings obj #f)))
;; if we have a last item with an unfulfilled :min value return it
(if (and (not (null? last))
(begin
(random-item-count-set! last
(+ 1 (random-item-count last)))
(< (random-item-count last)
(random-item-min last))))
(random-item-datum last)
(let ((range (pattern-cache obj))
(choices (car pool))
(pick (lambda (c r)
(do ((tail c (cdr tail))
(index (random r))
;; (index (ffi_ranfloat r ))
)
( (< index (random-item-index (car tail)))
(car tail)))))
(next #f))
(do ((item (pick choices range) (pick choices range)))
((not (and (random-item-max item)
(= (random-item-count item)
(random-item-max item))))
(set! next item))
)
(unless (eqv? next last)
(do ((tail choices (cdr tail)))
((null? tail) #f)
(random-item-count-set! (car tail) 0)))
(set-cdr! pool next)
;; adjust the weight of the newly selected item
(random-item-datum next)))))
;;; (define aaa (make-weighting '(a b c d e)))
;;; (next aaa #t)
;;; (define aaa (make-weighting '(a b (c :weight 10) d e)))
;;; (next aaa #t)
;;; (define aaa (make-weighting '(a b (c :min 4) d e)))
;;; (next aaa #t)
;;;
;;; markov
;;;
(define (canonicalize-markov-data data)
(define (parse-markov-spec spec)
(if (not (pair? spec))
(error "transition ~S is not a list" spec))
(let ((rhside (or (member '-> spec)
(member '#:-> spec)
(error "no right hand side in transition ~S"
spec)))
(lhside (list))
(range 0)
(outputs (list)))
;; separate lh and rh sides
(let* ((head (list #f))
(tail head))
(do ()
((eqv? spec rhside)
(set! lhside (cdr head))
(set! rhside (cdr rhside)))
(set-cdr! tail (list (car spec)))
(set! tail (cdr tail))
(set! spec (cdr spec))))
(for-each (lambda (s)
(let ((val #f)
(pat #f)
(wei #f))
(if (pair? s)
(begin (set! val (car s))
(set! wei (if (null? (cdr s)) 1 (cadr s)))
;; weight may be number or pattern
(set! pat wei)
(unless (number? wei)
(set! wei #f)))
(begin (set! val s) (set! wei 1) (set! pat 1)))
;; set range to #f if any weight is pattern
;; else precalc range for the constant weights
(if (and wei range)
(set! range (+ range wei))
(set! range #f))
;;(push (list val range pat) outputs)
(set! outputs (cons (list val range pat) outputs))
))
rhside)
(cons lhside (cons range (reverse outputs)))))
(let ((transitions (list #f)))
(do ((tail data (cdr tail))
(order #f)
(lis transitions)
(p #f))
((null? tail)
(cdr transitions) )
(set! p (parse-markov-spec (car tail)))
(if (not order)
(set! order (length (car p)))
;;(set! order (max order (length (first p))))
(if (not (= order (length (car p))))
(error "found left hand sides with different number of items in
~S"
data))
)
(set-cdr! lis (list p))
(set! lis (cdr lis)))))
;;; (parse-markov-spec '(a a -> b c ))
;;; (canonicalize-markov-data '((a a -> b c ) ( a b -> a) (c a -> c a)))
(define (make-markov data . args)
(if (not (pair? data))
(error "~S is not list of markov transitions" data)
(set! data (canonicalize-markov-data data)))
(with-optkeys (args for limit past)
(let* ((obj (%alloc-pattern))
(len (length data))
(flags 0))
(initialize-pattern obj data for limit
flags len len next-in-markov
(lambda (fn obj)
(for-each fn (pattern-data obj))))
(unless (pair? past)
(set! past (make-list (length (car (car data))) '*)))
(pattern-cache-set! obj past)
obj)))
(define (next-in-markov obj)
;; markov data kept as a list of lists. each list is in the form#:
;; ((<inputs>) range . <output>)
(letrec ((select-output
(lambda (range outputs)
;; if range is #f then one or more weights in the
;; outputs are patterns. in this case we map all the
;; outputs to update weights of every outcome and then
;; select. otherwise (range is number) we simply select
;; an outcome from the precalculated distribution.
(if (not range)
(do ((tail outputs (cdr tail))
(out #f)
(sum 0))
((null? tail)
(select-output sum outputs))
;; out is outcome#: (val rng <pat/wei>)
(set! out (car tail))
;; if third element is number use it else read it
(set! sum (+ sum (if (number? (caddr out))
(caddr out)
(next-1 (caddr out)))))
;; always update second element to new value
(set-car! (cdr out) sum))
(let (
(n (random range))
;; (n (ffi_ranfloat range))
)
(do ((tail outputs (cdr tail)))
((< n (cadr (car tail)))
(car (car tail)))))
)))
(match-past
(lambda (inputs past)
(do ((i inputs (cdr i))
(j past (cdr j))
(f #t))
((or (null? i) (null? j) (not f))
f)
(set! f (or (eqv? (car i) '*)
(equal? (car i) (car j))
(eqv? (car j) '*))))
)))
(do ((tail (pattern-data obj) (cdr tail))
(past (pattern-cache obj))
(item #f)
)
((or (null? tail) (null? past)
(match-past (car (car tail)) past))
(when (null? tail)
(error "no transition matches past ~S" past))
(set! item (select-output (cadr (car tail))
(cddr (car tail))))
(unless (null? past)
(if (null? (cdr past))
(set-car! past item)
(do ((last past (cdr last)))
((null? (cdr last))
;; rotate past choices leftward
(set-car! past item)
(set-cdr! last past)
(pattern-cache-set! obj (cdr past))
(set-cdr! (cdr last) (list))))))
item))
))
;;; (define aaa (make-markov '((a -> b c d) (b -> a) (c -> d) (d -> (a 3) b
c))))
;;; (next aaa 30)
(define (markov-analyze seq . args)
(let* ((morder #f) ; markov order
(result #f) ; what to return
(len (length seq))
(labels '()) ; the set of all outcomes
(table '())
(row-label-width 8)
(print-decimals 3)
(field (+ print-decimals 2))) ; n.nnn
(with-optkeys (args (order 1) (mode 1))
(set! morder order)
(set! result mode))
(unless (member result '(1 2 3))
(error "~S is not a valid mode value" result))
(letrec ((add-outcome
(lambda (prev next)
(let ((entry (find-if (lambda (x)
(equal? prev (car x)))
table)))
(if (not entry)
(set! table (cons (list prev
(format #f "~s" prev)
(list next 1))
table))
(let ((e (assoc next (cddr entry))))
(if e
(set-car! (cdr e) (+ 1 (cadr e)))
(set-cdr! (last (cdr entry))
(list (list next 1)))))))))
(before?
(lambda (x y l)
(if (null? x) #t
(let ((p1 (list-index (lambda (z) (equal? (car x) z))
l))
(p2 (list-index (lambda (z) (equal? (car y) z))
l)))
(cond ((< p1 p2) #t)
; bug!
;((= p1 p2) (before? (cdr x) (cdr y) l))
(else #f))))))
(liststring
(lambda (l)
(if (null? l) ""
(let ((a (format #f "~a" (car l))))
(do ((x (cdr l) (cdr x)))
((null? x) a)
(set! a
(string-append
a (format #f " ~a" (car x))))))))))
(do ((i 0 (+ i 1)))
((= i len) #f)
(do ((prev (list))
(j 0 (+ j 1)) ; j to morder
(x #f))
((> j morder)
(add-outcome (reverse prev) x )
(if (not (member x labels))
(set! labels (cons x labels))))
(set! x (list-ref seq (modulo (+ i j) len)))
;; gather history in reverse order
(when (< j morder) (set! prev (cons x prev)))))
;; sort the outcomes according to data
(cond ((number? (car labels))
(set! labels (sort labels <)))
((and (car labels) (symbol? (car labels)))
(set! labels (sort labels
(lambda (x y)
(string-ci<? (format #f "~a" x)
(format #f "~a" y))))))
(else
(set! labels (reverse labels))))
;; map over data, normalize weights
(do ((tail table (cdr tail))
(len 0))
((null? tail)
(set! row-label-width (max len row-label-width)) )
(let* ((row (car tail))
(lab (cadr row)) ; label
(val (cddr row)))
(set! len (max len (string-length lab)))
(let ((total (do ((e val (cdr e)) ; sum all e
(s 0))
((null? e) s)
(set! s (+ s (cadr (car e)))))))
(set! total (* total 1.0))
(do ((e val (cdr e)))
((null? e) #f)
(set-car! (cdr (car e))
(decimals (/ (cadr (car e)) total)
print-decimals))))))
;; sort table by labels
(set! table
(sort table (lambda (x y) (before? (car x) (car y) labels))))
;; print table
(when (eqv? result 1)
(let* ((port (open-output-string))
(sp " ")
(ln (make-string field #\-)))
;; print column header row
(newline port)
(do ((i 0 (+ i 1)))
((= i row-label-width) #f)
(write-char #\* port))
(do ((l labels (cdr l)))
((null? l) #f)
(display sp port) ;; column separator
(let* ((s (format #f "~a" (car l)))
(n (string-length s)))
;; write column pad
(do ((i 0 (+ i 1))
(m (max (- field n) 0)))
((= i m) #f)
(write-char #\space port))
(display s port)))
;; print each row
(do ((tail table (cdr tail)))
((null? tail) #f)
(let ((row (car tail)))
(newline port)
(let* ((s (liststring (car row)))
(n (string-length s)))
;; print left pad for row label
(do ((i 0 (+ i 1))
(m (max (- row-label-width n) 0)))
((= i m) #f)
(write-char #\space port))
;; print row label min row-label-width.
(do ((i 0 (+ i 1))
(m (min row-label-width n)))
((= i m) #f)
(write-char (string-ref s i) port)))
(do ((l labels (cdr l)))
((null? l) #f)
(let ((v (assoc (car l) (cddr row))))
(if (not v)
(begin (display sp port) (display ln port))
(let* ((s (number->string (cadr v)))
(n (string-length s)))
(display sp port)
;; s7: trim number to fit field
(if (>= n field)
(let ((d (position #\. s)))
(set! s (substring s 0 (min (+ d 4) n)))
(set! n (string-length s))))
;; pad number
(do ((i 0 (+ i 1))
(m (max (- field n) 0)))
((= i m) #f)
(write-char #\space port))
(display s port)
))))))
(newline port)
(display (get-output-string port))
(close-output-port port)
)))
(if (= result 1)
(values)
;; if returning pattern or data convert table to markov lists
(let ((pat (map (lambda (row)
(append (car row) '(->) (cddr row)))
table)))
(if (= result 2)
(make-markov pat)
pat)))))
; (define aaa '(c4 c4 d4 c4 f4 e4 c4 c4 d4 c4 g4 f4 c4 c4 c5 a4 f4 e4 d4 bf4
bf4 a4 f4 g4 f4))
; (define markovpat (markov-analyze aaa 1 2))
; (next markovpat 30)
;;;
;;; Graph
;;;
;; (define-record graph-node datum to id)
(define-record-type graph-node
(make-graph-node datum to id)
graph-node?
(datum graph-node-datum graph-node-datum-set!)
(to graph-node-to graph-node-to-set!)
(id graph-node-id graph-node-id-set!))
(define (pgraph-node obj port)
(list 'graph-node
(graph-node-datum obj) (graph-node-to obj)
(graph-node-id obj)))
(define (make-graph data . args)
(if (not (pair? data))
(error "~S is not a list of graph data" data)
(set! data (canonicalize-graph-data data)))
(with-optkeys (args for limit)
(let* ((obj (%alloc-pattern))
(len (length data))
(flags 0))
(initialize-pattern obj (cons #f data ) for limit
flags len len next-in-graph
(lambda (fn obj)
(for-each (lambda (n) ( fn (graph-node-datum n)))
(cdr (pattern-data obj)))))
obj)))
(define (canonicalize-graph-data data)
(let ((pos 1))
(define (parse-graph-item extern)
(unless (pair? extern)
(error "~S is not a graph node list" extern))
(apply (lambda (item . args)
(with-optkeys (args to id)
(unless id (set! id pos))
(set! pos (+ pos 1))
(make-graph-node item to id)))
extern))
(map parse-graph-item data)))
;; (canonicalize-graph-data '((a :to 2) (b :id 2 :to a)))
;; (canonicalize-graph-data '((a :id 1 :to b) (b :id 2 :to a)))
(define (next-in-graph obj)
(let* ((graph (pattern-data obj))
(nodes (cdr graph))
(this (car graph)))
(if (not this)
(begin
(set-car! graph (car nodes))
(graph-node-datum (car nodes)))
;; read the to: link and search for next node
(let ((link (next-1 (graph-node-to this)))
(next #f))
(do ((tail nodes (cdr tail)))
((or next (null? tail))
(if (not next)
(error "no graph node for id ~S" link)
(set-car! graph next))
(graph-node-datum next))
(if (eqv? link (graph-node-id (car tail)))
(set! next (car tail))))))))
;;; (define aaa (make-graph '((a :to b) (b :to a))))
;;; (next aaa)
;;; (define aaa (make-graph `((a :to 2) (b :id 2 :to 3) (c :id 3 :to
,(make-weighting '(1 2 3))))))
;;; (next aaa 20)
;;;
;;; Repeater
;;;
(define (make-repeater pat . args)
(with-optkeys (args for repeat limit)
(let ((obj (%alloc-pattern))
(flags 0)
)
(initialize-pattern obj (list) for stop
flags
0
1
next-in-repeater
(lambda (fn obj)
(for-each fn (pattern-data obj))))
;; pattern cache holds palin structure
(pattern-cache-set! obj (list pat repeat))
obj)))
(define (next-in-repeater obj)
(let ((data (pattern-data obj)))
(if (null? data)
(let* ((per (pattern-period obj))
(res (next (car (pattern-cache obj)) #t))
(len (length res))
(for (period-length per))
(rep (cadr (pattern-cache obj))))
(if rep
(begin
(set! for (next rep))
(period-length-set! per len)
(period-count-set! per len))
(period-count-set! per (* len for)))
(let ((sav res)
(don (- for 1)))
(do ((i 0 (+ i 1)))
((not (< i don)) #f)
(set! res (append res sav))))
(pattern-data-set! obj (cdr res))
(car res))
(begin
(pattern-data-set! obj (cdr data))
(car data)))))
-anders
_______________________________________________
Cmdist mailing list
[email protected]
http://ccrma-mail.stanford.edu/mailman/listinfo/cmdist