;;;; `vector-edit' ;;; Copyright MMIV-MMXV Arthur A. Gleckler. All rights reserved. ;; Permission is hereby granted, free of charge, to any person ;; obtaining a copy of this software and associated documentation ;; files (the "Software"), to deal in the Software without ;; restriction, including without limitation the rights to use, copy, ;; modify, merge, publish, distribute, sublicense, and/or sell copies ;; of the Software, and to permit persons to whom the Software is ;; furnished to do so, subject to the following conditions: ;; The above copyright notice and this permission notice shall be ;; included in all copies or substantial portions of the Software. ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;; DEALINGS IN THE SOFTWARE. ;;; `vector-edit' adds and/or removes elements from a vector ;;; non-destructively, i.e. by returning a new vector. It maps ;;; offsets in the original vector to offsets in the new vector so ;;; that the caller doesn't have to perform these error-prone ;;; calculations itself. (define (vector-without v start end) "Return a copy of vector `v' without the elements with indices [start, end)." (let* ((size (vector-length v)) (gap-size (- end start)) (new-size (- size gap-size)) (result (make-vector new-size))) (vector-copy! result 0 v 0 start) (vector-copy! result start v end size) result)) (define (vector-replace-one v i e) "Return a copy of vector `v' with the `i'th element replaced by `e'." (let ((result (vector-copy v))) (vector-set! result i e) result)) (define-syntax vector-edit-total-skew (syntax-rules (add drop) ((_ s) s) ((_ s (add i e) . rest) (vector-edit-total-skew (+ s 1) . rest)) ((_ s (drop i c) . rest) (vector-edit-total-skew (- s c) . rest)))) (define-syntax vector-edit-code (syntax-rules (add drop) ((_ v r o s) (let ((index (vector-length v))) (vector-copy! r (+ o s) v o index) r)) ((_ v r o s (add i e) . rest) (let ((index i)) (vector-copy! r (+ o s) v o index) (vector-set! r (+ s index) e) (let ((skew (+ s 1))) (vector-edit-code v r index skew . rest)))) ((_ v r o s (drop i c) . rest) (let ((index i)) (vector-copy! r (+ o s) v o index) (let* ((dropped c) (offset (+ index dropped)) (skew (- s dropped))) (vector-edit-code v r offset skew . rest)))))) ;; <> Optimize this by allowing one to supply more than one value in ;; `add' sub-expressions so that adjacent values can be inserted ;; without extra computation. ;; Given a vector `v' and a set of `(add i e)' and `(drop i c)' forms, ;; return a new vector that is the result of applying insertions to ;; and deletions from `v'. Interpret each `i' as an index into `v', ;; each `e' as an element to be inserted into the resulting vector at ;; the index corresponding to `i', and each `c' as a count of elements ;; of `v' to be dropped starting at index `i'. The `i' values in the ;; `add' and `drop' forms must never decrease from left to right. ;; This is useful for doing insertions and deletions without ;; constructing an intermediate vector. (define-syntax vector-edit (syntax-rules () ((_ v . rest) (let ((result (make-vector (+ (vector-length v) (vector-edit-total-skew 0 . rest))))) (vector-edit-code v result 0 0 . rest)))))