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