allow doc directory

pull/2/head
Matthew Butterick 10 years ago
parent e1a2fb7a3a
commit dfc91cca67

1
.gitignore vendored

@ -15,7 +15,6 @@ Icon
.Trashes
# generated documentation
doc/*
scribblings/*.js
scribblings/*.css
scribblings/*.html

@ -1,49 +1,46 @@
#lang racket/base
(require "define.rkt")
(require "coerce.rkt" "len.rkt" racket/list)
(require "coerce.rkt" "len.rkt" racket/list racket/set racket/sequence racket/stream racket/dict)
(define (sliceable-container? x)
(ormap (λ(proc) (proc x)) (list list? string? symbol? vector?)))
(ormap (λ(proc) (proc x)) (list list? string? symbol? vector? path? (λ(i) (and (not (dict? i)) (sequence? i))))))
(define (gettable-container? x)
(ormap (λ(proc) (proc x)) (list sliceable-container? hash?)))
(ormap (λ(proc) (proc x)) (list sliceable-container? dict?)))
(define+provide/contract (get container start [end #f])
((gettable-container? any/c) ((λ(i)(or (integer? i) (and (symbol? i) (equal? i 'end))))) . ->* . any/c)
(define/contract+provide (get container start [end #f])
((gettable-container? any/c) ((or/c (and/c integer? positive?) #f)) . ->* . any/c)
(define result
(with-handlers ([exn:fail? (λ(exn) (error (format "Couldn't get item from ~a" container)))])
(let ([end (if (sliceable-container? container)
(cond
;; treat negative lengths as offset from end (Python style)
[(and (integer? end) (< end 0)) (+ (len container) end)]
;; 'end slices to the end
[(equal? end 'end) (len container)]
;; default to slice length of 1 (i.e, single-item retrieval)
[(equal? end #f) (add1 start)]
[else end])
end)])
;; use handler to capture error & print localized error message
(with-handlers ([exn:fail? (λ(exn) (error (format "get: couldnt retrieve ~a from ~a" (if end (format "items ~a through ~a" start end) (format "item ~a" start)) container)))])
(let ([end (if (and (equal? end #f) (sliceable-container? container)) (add1 start) end)])
(cond
[(list? container) (for/list ([i (range start end)]) (list-ref container i))]
[(vector? container) (for/vector ([i (range start end)]) (vector-ref container i))]
[(vector? container) (for/vector ([i (range start end)]) (vector-ref container i))]
[(string? container) (substring container start end)]
[(symbol? container) (->symbol (get (->string container) start end))]
[(hash? container) (hash-ref container start)]
[(path? container) (get (explode-path container) start end)]
[(dict? container) (dict-ref container start)]
[(sequence? container) (get (->list container) start end)]
[else (error)]))))
;; don't return single-item results inside a list
(if (and (sliceable-container? container) (= (len result) 1))
(if (and (= (len result) 1) (sliceable-container? container))
(car (->list result))
result))
(define (listlike-container? container)
(ormap (λ(pred) (pred container)) (list vector? set? sequence?)))
(define+provide/contract (in? item container)
(define/contract+provide (in? item container)
(any/c any/c . -> . coerce/boolean?)
(cond
[(list? container) (member item container)] ; returns #f or sublist beginning with item
[(vector? container) (member item (vector->list container))] ; returns #f or sublist beginning with item
[(hash? container) (and (hash-has-key? container item) (get container item))] ; returns #f or hash value
[(string? container) (regexp-match (->string item) container)] ; returns #f or substring beginning with item
[(symbol? container) ((->string item) . in? . (->string container))] ; returns #f or subsymbol (?!) beginning with item
[else #f]))
[(list? container) (member item container)]
[(dict? container) (dict-has-key? container item)]
[(path? container) (in? (->path item) (explode-path container))]
[(stringish? container) (regexp-match (->string item) (->string container))]
;; location relevant because dicts and strings are also listlike (= sequences)
[(listlike-container? container) (in? item (->list container))]
[else #f]))

@ -8,6 +8,7 @@
[(list? x) (length x)]
[(string? x) (string-length x)]
[(symbol? x) (len (symbol->string x))]
[(path? x) (len (path->string x))]
[(vector? x) (vector-length x)]
[(hash? x) (len (hash-keys x))]
[(integer? x) (len (number->string x))]

@ -8,6 +8,8 @@
@title{Coercion}
@defmodule[sugar/coerce]
Functions that coerce the datatype of a value to another type. Racket already has type-specific conversion functions. But if you're handling values of indeterminate type — as sometimes happens in an untyped language — then handling the possible cases individually gets to be a drag.
@section{Values}
@defproc[

@ -0,0 +1,83 @@
#lang scribble/manual
@(require scribble/eval (for-label racket sugar))
@(define my-eval (make-base-eval))
@(my-eval `(require sugar))
@title{Container}
@defmodule[sugar/container]
Type-neutral functions for getting elements out of a container, or testing membership.
@defproc[
(get
[container (or/c list? vector? sequence? dict? string? symbol? path?)]
[which any/c]
[end_which (or/c (and/c integer? positive?) #f) #f])
any/c]
For a @racket[_container] that's a @racket[dict?], retrieve the element associated with the key @racket[_which]. Raise an error if the key doesn't exist.
@examples[#:eval my-eval
(get (make-hash '((a . 1) (b . 2) (c . 3))) 'b)
(get (make-hash '((a . 1) (b . 2) (c . 3))) 'z)
]
For other @racket[_container] types — which are all sequence-like — retrieve the element located at @racket[_which]. Or if the optional @racket[_end_which] argument is provided, retrieve the elements from @racket[_which] to @racket[(sub1 _end_which)], inclusive (i.e., make a slice). Raise an error if @racket[_which] or @racket[_end_which] is out of bounds.
@examples[#:eval my-eval
(get '(0 1 2 3 4 5) 2)
(get '(0 1 2 3 4 5) 2 4)
(get '(0 1 2 3 4 5) 100)
(get '(0 1 2 3 4 5) 2 100)
(get (list->vector '(0 1 2 3 4 5)) 2)
(get (list->vector '(0 1 2 3 4 5)) 2 4)
(get "purple" 2)
(get "purple" 2 4)
(get 'purple 2)
(get 'purple 2 4)
]
When @racket[_container] is a path, it's treated as a list of exploded path elements, not as a stringlike value.
@examples[#:eval my-eval
(get (string->path "/root/foo/bar/file.txt") 1)
(get (string->path "/root/foo/bar/file.txt") 0 3)
]
To slice to the end of @racket[_container], use @racket[(len _container)] as the value of @racket[_end_which].
@examples[#:eval my-eval
(define xs '(0 1 2 3 4 5))
(get xs 2 (len xs))
(get (list->vector xs) 2 (len (list->vector xs)))
(define color "purple")
(get color 2 (len color))
]
@defproc[
(in?
[item any/c]
[container (or/c list? vector? sequence? set? dict? string? symbol? path?)])
boolean?]
Return @racket[#t] if @racket[_item] is in @racket[_container], or @racket[#f] otherwise.
@examples[#:eval my-eval
(in? 2 '(0 1 2 3 4 5))
(in? 'a '(0 1 2 3 4 5))
(in? 2 (list->vector '(0 1 2 3 4 5)))
(in? "pu" "purple")
(in? "zig" "purple")
(in? 'b (make-hash '((a . 1) (b . 2) (c . 3))))
(in? 'z (make-hash '((a . 1) (b . 2) (c . 3))))
]
As with @racket[get], when @racket[_container] is a path, it's treated as a list of exploded path elements, not as a stringlike value.
@examples[#:eval my-eval
(in? "foo" (string->path "/root/foo/bar/file.txt"))
(in? "zam" (string->path "/root/foo/bar/file.txt"))
]

@ -47,23 +47,23 @@
(check-equal? (get '(0 1 2 3 4 5) 2) 2)
(check-exn exn:fail? (λ() (get '(0 1 2 3 4 5) 100))) ; index too big
(check-equal? (get `(0 1 ,(list 2) 3 4 5) 2) (list 2))
(check-equal? (get '(0 1 2 3 4 5) 0 2) '(0 1))
(check-equal? (get '(0 1 2 3 4 5) 2 -1) '(2 3 4))
(check-equal? (get '(0 1 2 3 4 5) 2 'end) '(2 3 4 5))
(check-equal? (get (list->vector '(0 1 2 3 4 5)) 2) 2)
(check-equal? (get (list->vector'(0 1 2 3 4 5)) 0 2) (list->vector '(0 1)))
(check-equal? (get (list->vector'(0 1 2 3 4 5)) 2 -1) (list->vector '(2 3 4)))
(check-equal? (get (list->vector'(0 1 2 3 4 5)) 2 'end) (list->vector '(2 3 4 5)))
(check-equal? (get "purple" 2) "r")
(check-equal? (get "purple" 0 2) "pu")
(check-equal? (get "purple" 2 -1) "rpl")
(check-equal? (get "purple" 2 'end) "rple")
(check-equal? (get 'purple 2) 'r)
(check-equal? (get 'purple 0 2) 'pu)
(check-equal? (get 'purple 2 -1) 'rpl)
(check-equal? (get 'purple 2 'end) 'rple)
(check-equal? (get (string->path "/root/foo/bar/file.txt") 2) (string->path "foo"))
(check-equal? (get (string->path "/root/foo/bar/file.txt") 0 2) (list (string->path "/") (string->path "root")))
(check-equal? (get (make-hash `((a . ,(list 1)) (b . ,(list 2)) (c . ,(list 3)))) 'a) (list 1))
(check-exn exn:fail? (λ() (get (make-hash `((a . ,(list 1)) (b . ,(list 2)) (c . ,(list 3)))) 'z))) ; nonexistent key
(check-equal? (get (string->path "/root/foo/bar/file.txt") 1) (string->path "root"))
(check-equal? (get (string->path "/root/foo/bar/file.txt") 0 3)
(map string->path '("/" "root" "foo")))
(check-true (2 . in? . '(1 2 3)))
@ -76,7 +76,10 @@
(check-false ("z" . in? . "foobar"))
(check-true ('o . in? . 'foobar))
(check-false ('z . in? . 'foobar))
(check-false ("F" . in? . #\F))
(check-true ("F" . in? . #\F))
(check-true (in? "foo" (string->path "/root/foo/bar/file.txt")))
(check-false (in? "zam" (string->path "/root/foo/bar/file.txt")))
(check-true ("foobar" . starts-with? . "foo"))

Loading…
Cancel
Save