You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sugar/container.rkt

47 lines
2.3 KiB
Racket

#lang racket/base
(require "define.rkt" "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? path? (λ(i) (and (not (dict? i)) (sequence? i))))))
(define (gettable-container? x)
(ormap (λ(proc) (proc x)) (list sliceable-container? dict?)))
(define+provide+safe (get container start [end #f])
((gettable-container? any/c) ((or/c (and/c integer? positive?) #f)) . ->* . any)
(define result
;; use handler to capture error & print localized error message
(with-handlers ([exn:fail? (λ(exn) (error (format "get: couldn't 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 (in-range start end)]) (list-ref container i))]
[(vector? container) (for/vector ([i (in-range start end)]) (vector-ref container i))]
[(string? container) (substring container start end)]
[(symbol? container) (->symbol (get (->string container) start end))]
[(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
;; check for integer because integers don't have length
(if (and (not (integer? result)) (= (len result) 1) (sliceable-container? container))
(car (->list result))
result))
(define (listlike-container? container)
(ormap (λ(pred) (pred container)) (list vector? set? sequence?)))
(define+provide+safe (in? item container)
(any/c any/c . -> . boolean?)
(->boolean (cond
[(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])))