chibi-scheme/lib/srfi/146/vector-edit.scm
2020-07-28 15:29:49 +09:00

93 lines
No EOL
3.7 KiB
Scheme

;;;; `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)))))