--- title: "flexseq" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{flexseq} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") if(requireNamespace("pkgload", quietly = TRUE)) { pkgload::load_all(".", quiet = TRUE) } else if(requireNamespace("Immutables", quietly = TRUE)) { library(Immutables) } else { stop("Need either installed 'Immutables' or the 'pkgload' package to render this vignette.") } ``` ## `flexseq` basics `flexseq` is a persistent sequence type, with many of the same features as an R `list()`, including storing arbitrary items. All updates return a new object and keep the original unchanged. ```{r} x <- flexseq(1, 2, 3) x x2 <- as_flexseq(letters[1:5]) x2 ``` ## Names and indexing Flexseqs may be named, but if any are named they must all have unique non-`NULL` names. Large named flexseqs are somewhat slower than unnamed ones; for efficient key/value stores consider using an `ordered_sequence()`. ```{r} x <- flexseq(a = 1, b = 2, c = 3) x x[c("c", "b")] x$b <- NULL # delete b x ``` In general indexing and slicing works as it does with lists: `[]` returns a sublist indexed by integer (by position), character vector (by name if named), or logical vector (by inclusion). Note that these operations are $O(k\log n)$ where $k$ is the length of the indexing vector. ```{r} x <- as_flexseq(letters[1:6]) x[[3]] x[c(1, 3, 5)] ``` ## End operations Flexseqs support pushing new elements onto the front or back of the sequences, peeking at the front or back (i.e., getting a copy of the first or last item), and popping from the front or back, which returns the removed element and a copy of the flexseq input with that item removed. `pop_front()` and `pop_back()` both return a list with fields `$value` and `$remaining`; `peek_back()` is the symmetric counterpart to `peek_front()`. ```{r} x <- as_flexseq(4:6) x <- x |> push_back(100) |> push_front(50) x xpopped <- pop_front(x) xpopped$value xpopped$remaining ``` ## Positional operations Beyond the ends, flexseqs support arbitrary-position persistent operations. `peek_at()` is the non-throwing positional read (compare with `[[`, which errors when out of bounds); `pop_at()` removes an element at a given index and returns `$value`/`$remaining`; `insert_at()` inserts before a given index, or at `length(x) + 1` to append. ```{r} x <- flexseq("a", "b", "c", "d") peek_at(x, 10) # NULL, no error out <- pop_at(x, 2) out$value out$remaining insert_at(x, 3, c("x", "y")) ``` ## Transforming and combining `c()` concatenates flexseqs, and `fapply()` applies a function to each element of one returning the result as a flexseq, analygous to `lapply()` for R lists. ```{r} x <- as_flexseq(4:6) x2 <- as_flexseq(8:10) c(x, x2) x <- as_flexseq(1:3) fapply(x, function(el) el * 10) ``` `merge(x, y)` is provided as an alias for `c(x, y)` so that `merge()` works uniformly across the package's four structure types. For `flexseq`, `c()` and `merge()` are equivalent; the distinction matters for `ordered_sequence`, `interval_index`, and `priority_queue`, where `merge()` performs a proper sorted/priority-aware combine. `loop()` (re-exported from the **coro** package) enables `for`-loop traversal, yielding elements left-to-right lazily without materializing a list. ```{r} x <- flexseq("a", "b", "c", "d") loop(for (el in x) { print(el) }) ``` For named flexseqs, `loop()` yields bare values (matching `peek_front()`); use `as.list()` when you need names alongside values. ```{r} x <- flexseq(a = 1, b = 2, c = 3) loop(for (el in x) print(el)) ``` Plain `for (el in x)` (without `loop()`) does *not* dispatch to the iteration protocol — it walks the underlying finger-tree storage and yields internal nodes rather than sequence elements. Always wrap with `loop()`. Flexseqs convert back to standard R structures with `as.list()` (preserves names and list semantics) and `unlist()` (atomic vector when possible). `length()` returns the element count in O(1), and `str()` gives a compact diagnostic display. ```{r} x <- flexseq(a = 1, b = 2, c = 3) as.list(x) unlist(x) length(x) ```