add *list/c
This commit is contained in:
parent
5214b06a86
commit
856e60fe51
|
@ -552,6 +552,29 @@ each element of the list must match the corresponding contract. Beware
|
||||||
that when this contract is applied to a value, the result is not
|
that when this contract is applied to a value, the result is not
|
||||||
necessarily @racket[eq?] to the input.}
|
necessarily @racket[eq?] to the input.}
|
||||||
|
|
||||||
|
@defproc[(*list/c [prefix contract?] [suffix contract?] ...) list-contract?]{
|
||||||
|
|
||||||
|
Produces a contract for a list. The number of elements in the list
|
||||||
|
must be at least as long as the number of @racket[suffix] contracts
|
||||||
|
and the tail of the list must match those contracts, one for each
|
||||||
|
element. The beginning portion of the list can be arbitrarily long,
|
||||||
|
and each element must match @racket[prefix].
|
||||||
|
|
||||||
|
Beware that when this contract is applied to a value, the result is not
|
||||||
|
necessarily @racket[eq?] to the input.
|
||||||
|
|
||||||
|
@examples[#:eval (contract-eval) #:once
|
||||||
|
(define/contract a-list-of-numbers-ending-with-two-integers
|
||||||
|
(*list/c number? integer? integer?)
|
||||||
|
(list 1/2 4/5 +1i -11 322))
|
||||||
|
|
||||||
|
(eval:error
|
||||||
|
(define/contract not-enough-integers-at-the-end
|
||||||
|
(*list/c number? integer? integer? integer?)
|
||||||
|
(list 1/2 4/5 1/2 321 322)))]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@defproc[(syntax/c [c flat-contract?]) flat-contract?]{
|
@defproc[(syntax/c [c flat-contract?]) flat-contract?]{
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,12 @@
|
||||||
(ctest #t contract-first-order-passes? (non-empty-listof integer?) (list 1))
|
(ctest #t contract-first-order-passes? (non-empty-listof integer?) (list 1))
|
||||||
(ctest #f contract-first-order-passes? (non-empty-listof integer?) (list))
|
(ctest #f contract-first-order-passes? (non-empty-listof integer?) (list))
|
||||||
|
|
||||||
|
(ctest #t contract-first-order-passes? (*list/c integer? boolean? char?) '(1 2 3 4 #f #\a))
|
||||||
|
(ctest #t contract-first-order-passes? (*list/c integer? boolean? char?) '(#f #\a))
|
||||||
|
(ctest #f contract-first-order-passes? (*list/c integer? boolean? char?) '(1 2 #f 4 #f #\a))
|
||||||
|
(ctest #f contract-first-order-passes? (*list/c integer? boolean? char?) '())
|
||||||
|
(ctest #f contract-first-order-passes? (*list/c integer? boolean? char?) '(#f))
|
||||||
|
(ctest #f contract-first-order-passes? (*list/c integer? boolean? char?) 1)
|
||||||
|
|
||||||
(ctest #t contract-first-order-passes?
|
(ctest #t contract-first-order-passes?
|
||||||
(vector-immutableof integer?)
|
(vector-immutableof integer?)
|
||||||
|
|
|
@ -159,6 +159,18 @@
|
||||||
c))
|
c))
|
||||||
#t)
|
#t)
|
||||||
|
|
||||||
|
(test/spec-passed/result
|
||||||
|
'list-contract-25
|
||||||
|
'(list-contract?
|
||||||
|
(*list/c integer? boolean? char?))
|
||||||
|
#t)
|
||||||
|
|
||||||
|
(test/spec-passed/result
|
||||||
|
'list-contract-26
|
||||||
|
'(list-contract?
|
||||||
|
(*list/c (-> integer? integer?) boolean? char?))
|
||||||
|
#t)
|
||||||
|
|
||||||
(test/pos-blame
|
(test/pos-blame
|
||||||
'test-contract-25
|
'test-contract-25
|
||||||
'(contract (letrec ([c (recursive-contract (first-or/c (cons/c any/c c) empty?)
|
'(contract (letrec ([c (recursive-contract (first-or/c (cons/c any/c c) empty?)
|
||||||
|
|
|
@ -171,4 +171,39 @@
|
||||||
'(contract? (cons/dc [hd integer?] [tl (hd) integer?] #:impersonator))
|
'(contract? (cons/dc [hd integer?] [tl (hd) integer?] #:impersonator))
|
||||||
#t)
|
#t)
|
||||||
|
|
||||||
|
(test/spec-passed/result
|
||||||
|
'*list/c1
|
||||||
|
'(contract (*list/c integer? char? boolean?) '(1 2 3 #\a #f) 'pos 'neg)
|
||||||
|
'(1 2 3 #\a #f))
|
||||||
|
(test/pos-blame
|
||||||
|
'*list/c2
|
||||||
|
'(contract (*list/c integer? char? boolean?) '(1 2 #\a #\a #f) 'pos 'neg))
|
||||||
|
(test/spec-passed/result
|
||||||
|
'*list/c3
|
||||||
|
'((car (contract (*list/c (-> integer? integer?) (-> boolean? boolean?))
|
||||||
|
(list (λ (x) x) (λ (y) y)) 'pos 'neg))
|
||||||
|
1)
|
||||||
|
1)
|
||||||
|
(test/neg-blame
|
||||||
|
'*list/c4
|
||||||
|
'((car (contract (*list/c (-> integer? integer?) (-> boolean? boolean?))
|
||||||
|
(list (λ (x) x) (λ (y) y)) 'pos 'neg))
|
||||||
|
#f))
|
||||||
|
(test/spec-passed/result
|
||||||
|
'*list/c5
|
||||||
|
'((cadr (contract (*list/c (-> integer? integer?) (-> boolean? boolean?))
|
||||||
|
(list (λ (x) x) (λ (y) y)) 'pos 'neg))
|
||||||
|
#f)
|
||||||
|
#f)
|
||||||
|
(test/neg-blame
|
||||||
|
'*list/c6
|
||||||
|
'((cadr (contract (*list/c (-> integer? integer?) (-> boolean? boolean?))
|
||||||
|
(list (λ (x) x) (λ (y) y)) 'pos 'neg))
|
||||||
|
1))
|
||||||
|
(test/pos-blame
|
||||||
|
'*list/c7
|
||||||
|
'((caddr (contract (*list/c (-> integer? integer?) (-> boolean? boolean?) (-> char? char?))
|
||||||
|
(list (λ (x) x) (λ (y) y) (λ (y) 'not-a-bool) (λ (y) y)) 'pos 'neg))
|
||||||
|
#f))
|
||||||
|
|
||||||
)
|
)
|
|
@ -79,6 +79,8 @@
|
||||||
(check-not-exn (λ () (test-contract-generation (list*of boolean? char?))))
|
(check-not-exn (λ () (test-contract-generation (list*of boolean? char?))))
|
||||||
(check-not-exn (λ () (test-contract-generation (list/c boolean? number?))))
|
(check-not-exn (λ () (test-contract-generation (list/c boolean? number?))))
|
||||||
(check-not-exn (λ () ((car (test-contract-generation (list/c (-> number? number?)))) 0)))
|
(check-not-exn (λ () ((car (test-contract-generation (list/c (-> number? number?)))) 0)))
|
||||||
|
(check-not-exn (λ () (test-contract-generation (*list/c boolean? number? char?))))
|
||||||
|
(check-not-exn (λ () (test-contract-generation (-> (*list/c boolean? number? char?) any))))
|
||||||
|
|
||||||
(check-not-exn
|
(check-not-exn
|
||||||
(λ ()
|
(λ ()
|
||||||
|
|
|
@ -305,6 +305,10 @@
|
||||||
(ctest #t contract-stronger? (list/c (<=/c 3)) (non-empty-listof (<=/c 5)))
|
(ctest #t contract-stronger? (list/c (<=/c 3)) (non-empty-listof (<=/c 5)))
|
||||||
(ctest #f contract-stronger? (list/c) (non-empty-listof (<=/c 5)))
|
(ctest #f contract-stronger? (list/c) (non-empty-listof (<=/c 5)))
|
||||||
(ctest #t contract-stronger? (list/c) (listof (<=/c 5)))
|
(ctest #t contract-stronger? (list/c) (listof (<=/c 5)))
|
||||||
|
|
||||||
|
(ctest #t contract-stronger? (*list/c integer? boolean? char?) (*list/c integer? boolean? char?))
|
||||||
|
(ctest #t contract-stronger? (list/c integer? boolean? char?) (listof (or/c integer? boolean? char?)))
|
||||||
|
|
||||||
(ctest #t contract-stronger? (promise/c (<=/c 2)) (promise/c (<=/c 3)))
|
(ctest #t contract-stronger? (promise/c (<=/c 2)) (promise/c (<=/c 3)))
|
||||||
(ctest #f contract-stronger? (promise/c (<=/c 3)) (promise/c (<=/c 2)))
|
(ctest #f contract-stronger? (promise/c (<=/c 3)) (promise/c (<=/c 2)))
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,7 @@
|
||||||
false/c
|
false/c
|
||||||
printable/c
|
printable/c
|
||||||
listof list*of non-empty-listof cons/c list/c cons/dc
|
listof list*of non-empty-listof cons/c list/c cons/dc
|
||||||
|
*list/c
|
||||||
promise/c
|
promise/c
|
||||||
syntax/c
|
syntax/c
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
(provide listof list*of non-empty-listof cons/c list/c cons/dc
|
(provide listof list*of non-empty-listof cons/c list/c cons/dc
|
||||||
blame-add-car-context
|
blame-add-car-context
|
||||||
blame-add-cdr-context
|
blame-add-cdr-context
|
||||||
raise-not-cons-blame-error)
|
raise-not-cons-blame-error
|
||||||
|
*list/c)
|
||||||
|
|
||||||
(define (listof-generate ctc)
|
(define (listof-generate ctc)
|
||||||
(cond
|
(cond
|
||||||
|
@ -779,6 +780,197 @@
|
||||||
#:late-neg-projection list/c-chaperone/other-late-neg-projection
|
#:late-neg-projection list/c-chaperone/other-late-neg-projection
|
||||||
#:list-contract? (λ (c) #t)))
|
#:list-contract? (λ (c) #t)))
|
||||||
|
|
||||||
|
(define (*list/c-name-proc ctc)
|
||||||
|
`(*list/c ,(contract-name (*list-ctc-prefix ctc))
|
||||||
|
,@(map contract-name (*list-ctc-suffix ctc))))
|
||||||
|
|
||||||
|
(define (*list/c-first-order ctc)
|
||||||
|
(define prefix? (contract-first-order (*list-ctc-prefix ctc)))
|
||||||
|
(define suffix?s (map contract-first-order (*list-ctc-suffix ctc)))
|
||||||
|
(define suffix?s-len (length suffix?s))
|
||||||
|
(λ (val)
|
||||||
|
(cond
|
||||||
|
[(list? val)
|
||||||
|
(define-values (long-enough? end) (get-end val suffix?s-len))
|
||||||
|
(cond
|
||||||
|
[long-enough?
|
||||||
|
(let loop ([val val]
|
||||||
|
[end end])
|
||||||
|
(cond
|
||||||
|
[(null? end)
|
||||||
|
(for/and ([ele (in-list val)]
|
||||||
|
[suffix? (in-list suffix?s)])
|
||||||
|
(suffix? ele))]
|
||||||
|
[else
|
||||||
|
(and (prefix? (car val))
|
||||||
|
(loop (cdr val) (cdr end)))]))]
|
||||||
|
[else #f])]
|
||||||
|
[else #f])))
|
||||||
|
|
||||||
|
(define (get-end val suffix?s-len)
|
||||||
|
(let loop ([val val]
|
||||||
|
[i suffix?s-len])
|
||||||
|
(cond
|
||||||
|
[(zero? i) (values #t val)]
|
||||||
|
[(null? val) (values #f #f)]
|
||||||
|
[else (loop (cdr val) (- i 1))])))
|
||||||
|
|
||||||
|
(define (*list/c-generate ctc)
|
||||||
|
(λ (fuel)
|
||||||
|
(define prefix-gen (contract-random-generate/choose (*list-ctc-prefix ctc) fuel))
|
||||||
|
(define suffix-gens
|
||||||
|
(for/list ([suf (*list-ctc-suffix ctc)])
|
||||||
|
(contract-random-generate/choose suf fuel)))
|
||||||
|
(and prefix-gen
|
||||||
|
(for/and ([i (in-list suffix-gens)]) i)
|
||||||
|
(λ ()
|
||||||
|
(let loop ()
|
||||||
|
(rand-choice
|
||||||
|
[1/5 (for/list ([suffix-gen (in-list suffix-gens)])
|
||||||
|
(suffix-gen))]
|
||||||
|
[else (cons (prefix-gen) (loop))]))))))
|
||||||
|
|
||||||
|
(define (*list/c-exercise ctc)
|
||||||
|
(define suffix (reverse (*list-ctc-suffix ctc)))
|
||||||
|
(λ (fuel)
|
||||||
|
(define env (contract-random-generate-get-current-environment))
|
||||||
|
(values
|
||||||
|
(λ (lst)
|
||||||
|
(let loop ([lst (reverse lst)]
|
||||||
|
[suffix suffix])
|
||||||
|
(cond
|
||||||
|
[(null? suffix) (void)]
|
||||||
|
[else
|
||||||
|
(contract-random-generate-stash env (car lst) (car suffix))
|
||||||
|
(loop (cdr lst) (cdr suffix))])))
|
||||||
|
suffix)))
|
||||||
|
|
||||||
|
(define (*list/c-stronger this that)
|
||||||
|
(define this-prefix (*list-ctc-prefix this))
|
||||||
|
(define this-suffix (*list-ctc-suffix this))
|
||||||
|
(cond
|
||||||
|
[(*list-ctc? that)
|
||||||
|
(define that-prefix (*list-ctc-prefix that))
|
||||||
|
(define that-suffix (*list-ctc-suffix that))
|
||||||
|
(define (pad-to a-suf b-suf a-prefix)
|
||||||
|
(define a-len (length a-suf))
|
||||||
|
(define b-len (length b-suf))
|
||||||
|
(cond
|
||||||
|
[(< a-len b-len)
|
||||||
|
(append (build-list (- b-len a-len) (λ (x) a-prefix))
|
||||||
|
a-suf)]
|
||||||
|
[else a-suf]))
|
||||||
|
(define padded-this (pad-to this-suffix that-suffix this-prefix))
|
||||||
|
(define padded-that (pad-to that-suffix this-suffix that-prefix))
|
||||||
|
(and (contract-struct-stronger? this-prefix that-prefix)
|
||||||
|
(for/and ([this (in-list padded-this)]
|
||||||
|
[that (in-list padded-that)])
|
||||||
|
(contract-struct-stronger? this that)))]
|
||||||
|
[(listof-ctc? that)
|
||||||
|
(define that-elem (listof-ctc-elem-c that))
|
||||||
|
(and (contract-struct-stronger? this-prefix that-elem)
|
||||||
|
(for/and ([suf (in-list this-suffix)])
|
||||||
|
(contract-struct-stronger? suf that-elem)))]
|
||||||
|
[else #f]))
|
||||||
|
|
||||||
|
(define (*list/c-late-neg-projection ctc flat?)
|
||||||
|
(define prefix-lnp (contract-late-neg-projection (*list-ctc-prefix ctc)))
|
||||||
|
(define suffix-lnps (map contract-late-neg-projection (*list-ctc-suffix ctc)))
|
||||||
|
(define suffix?s-len (length suffix-lnps))
|
||||||
|
(λ (blame)
|
||||||
|
(define prefix-val-acceptor (prefix-lnp (blame-add-context blame "the prefix of")))
|
||||||
|
(define suffix-val-acceptors
|
||||||
|
(for/list ([i (in-naturals)]
|
||||||
|
[suffix-lnp (in-list suffix-lnps)])
|
||||||
|
(define which (- suffix?s-len i))
|
||||||
|
(define msg
|
||||||
|
(if (= 1 which)
|
||||||
|
"the last element of"
|
||||||
|
(format "the ~a to the last element of" (n->th which))))
|
||||||
|
(suffix-lnp (blame-add-context blame msg))))
|
||||||
|
(λ (val neg-party)
|
||||||
|
(cond
|
||||||
|
[(list? val)
|
||||||
|
(define-values (long-enough? end) (get-end val suffix?s-len))
|
||||||
|
(cond
|
||||||
|
[long-enough?
|
||||||
|
(let loop ([remainder-to-process val]
|
||||||
|
[end end])
|
||||||
|
(cond
|
||||||
|
[(null? end)
|
||||||
|
(cond
|
||||||
|
[flat?
|
||||||
|
(for ([ele (in-list remainder-to-process)]
|
||||||
|
[suffix-val-acceptor (in-list suffix-val-acceptors)])
|
||||||
|
(suffix-val-acceptor ele neg-party))
|
||||||
|
val]
|
||||||
|
[else
|
||||||
|
(for/list ([ele (in-list remainder-to-process)]
|
||||||
|
[suffix-val-acceptor (in-list suffix-val-acceptors)])
|
||||||
|
(suffix-val-acceptor ele neg-party))])]
|
||||||
|
[else
|
||||||
|
(define fst (prefix-val-acceptor (car remainder-to-process) neg-party))
|
||||||
|
(if flat?
|
||||||
|
(loop (cdr remainder-to-process) (cdr end))
|
||||||
|
(cons fst (loop (cdr remainder-to-process) (cdr end))))]))]
|
||||||
|
[else
|
||||||
|
(raise-blame-error
|
||||||
|
blame
|
||||||
|
val
|
||||||
|
'(expected: "list? with at least ~a elements" given: "~e")
|
||||||
|
suffix?s-len
|
||||||
|
val)])]
|
||||||
|
[else (raise-blame-error
|
||||||
|
blame
|
||||||
|
val
|
||||||
|
'(expected: "list?" given: "~e") val)]))))
|
||||||
|
|
||||||
|
;; prefix : contract
|
||||||
|
;; suffix : (listof contract)
|
||||||
|
(struct *list-ctc (prefix suffix)
|
||||||
|
#:property prop:custom-write custom-write-property-proc)
|
||||||
|
|
||||||
|
(struct flat-*list/c *list-ctc ()
|
||||||
|
#:property prop:contract
|
||||||
|
(build-contract-property
|
||||||
|
#:name *list/c-name-proc
|
||||||
|
#:first-order *list/c-first-order
|
||||||
|
#:generate *list/c-generate
|
||||||
|
#:exercise *list/c-exercise
|
||||||
|
#:stronger *list/c-stronger
|
||||||
|
#:late-neg-projection (λ (ctc) (*list/c-late-neg-projection ctc #t))
|
||||||
|
#:list-contract? (λ (c) #t)))
|
||||||
|
(struct chaperone-*list/c *list-ctc ()
|
||||||
|
#:property prop:contract
|
||||||
|
(build-contract-property
|
||||||
|
#:name *list/c-name-proc
|
||||||
|
#:first-order *list/c-first-order
|
||||||
|
#:generate *list/c-generate
|
||||||
|
#:exercise *list/c-exercise
|
||||||
|
#:stronger *list/c-stronger
|
||||||
|
#:late-neg-projection (λ (ctc) (*list/c-late-neg-projection ctc #f))
|
||||||
|
#:list-contract? (λ (c) #t)))
|
||||||
|
(struct impersonator-*list/c *list-ctc ()
|
||||||
|
#:property prop:contract
|
||||||
|
(build-contract-property
|
||||||
|
#:name *list/c-name-proc
|
||||||
|
#:first-order *list/c-first-order
|
||||||
|
#:generate *list/c-generate
|
||||||
|
#:exercise *list/c-exercise
|
||||||
|
#:stronger *list/c-stronger
|
||||||
|
#:late-neg-projection (λ (ctc) (*list/c-late-neg-projection ctc #f))
|
||||||
|
#:list-contract? (λ (c) #t)))
|
||||||
|
|
||||||
|
(define (*list/c ele . rest)
|
||||||
|
(define ctcs (coerce-contracts '*list/c (cons ele rest)))
|
||||||
|
(cond
|
||||||
|
[(null? rest) (listof ele)]
|
||||||
|
[(andmap flat-contract? ctcs)
|
||||||
|
(flat-*list/c (car ctcs) (cdr ctcs))]
|
||||||
|
[(andmap chaperone-contract? ctcs)
|
||||||
|
(chaperone-*list/c (car ctcs) (cdr ctcs))]
|
||||||
|
[else
|
||||||
|
(impersonator-*list/c (car ctcs) (cdr ctcs))]))
|
||||||
|
|
||||||
;; this is a hack to work around cyclic linking issues;
|
;; this is a hack to work around cyclic linking issues;
|
||||||
;; see definition of set-some-basic-contracts!
|
;; see definition of set-some-basic-contracts!
|
||||||
|
|
Loading…
Reference in New Issue
Block a user