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.
x <- flexseq(1, 2, 3)
x
#> Unnamed flexseq with 3 elements.
#>
#> Elements:
#>
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 2
#>
#> [[3]]
#> [1] 3
x2 <- as_flexseq(letters[1:5])
x2
#> Unnamed flexseq with 5 elements.
#>
#> Elements:
#>
#> [[1]]
#> [1] "a"
#>
#> [[2]]
#> [1] "b"
#>
#> ... (skipping 1 element)
#>
#> [[4]]
#> [1] "d"
#>
#> [[5]]
#> [1] "e"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().
x <- flexseq(a = 1, b = 2, c = 3)
x
#> Named flexseq with 3 elements.
#>
#> Elements:
#>
#> $a
#> [1] 1
#>
#> $b
#> [1] 2
#>
#> $c
#> [1] 3
x[c("c", "b")]
#> Named flexseq with 2 elements.
#>
#> Elements:
#>
#> $c
#> [1] 3
#>
#> $b
#> [1] 2
x$b <- NULL # delete b
x
#> Named flexseq with 2 elements.
#>
#> Elements:
#>
#> $a
#> [1] 1
#>
#> $c
#> [1] 3In 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
where
is the length of the indexing vector.
x <- as_flexseq(letters[1:6])
x[[3]]
#> [1] "c"
x[c(1, 3, 5)]
#> Unnamed flexseq with 3 elements.
#>
#> Elements:
#>
#> [[1]]
#> [1] "a"
#>
#> [[2]]
#> [1] "c"
#>
#> [[3]]
#> [1] "e"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().
x <- as_flexseq(4:6)
x <- x |>
push_back(100) |>
push_front(50)
x
#> Unnamed flexseq with 5 elements.
#>
#> Elements:
#>
#> [[1]]
#> [1] 50
#>
#> [[2]]
#> [1] 4
#>
#> ... (skipping 1 element)
#>
#> [[4]]
#> [1] 6
#>
#> [[5]]
#> [1] 100
xpopped <- pop_front(x)
xpopped$value
#> [1] 50
xpopped$remaining
#> Unnamed flexseq with 4 elements.
#>
#> Elements:
#>
#> [[1]]
#> [1] 4
#>
#> [[2]]
#> [1] 5
#>
#> [[3]]
#> [1] 6
#>
#> [[4]]
#> [1] 100Positional 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.
x <- flexseq("a", "b", "c", "d")
peek_at(x, 10) # NULL, no error
#> NULL
out <- pop_at(x, 2)
out$value
#> [1] "b"
out$remaining
#> Unnamed flexseq with 3 elements.
#>
#> Elements:
#>
#> [[1]]
#> [1] "a"
#>
#> [[2]]
#> [1] "c"
#>
#> [[3]]
#> [1] "d"
insert_at(x, 3, c("x", "y"))
#> Unnamed flexseq with 6 elements.
#>
#> Elements:
#>
#> [[1]]
#> [1] "a"
#>
#> [[2]]
#> [1] "b"
#>
#> ... (skipping 2 elements)
#>
#> [[5]]
#> [1] "c"
#>
#> [[6]]
#> [1] "d"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.
x <- as_flexseq(4:6)
x2 <- as_flexseq(8:10)
c(x, x2)
#> Unnamed flexseq with 6 elements.
#>
#> Elements:
#>
#> [[1]]
#> [1] 4
#>
#> [[2]]
#> [1] 5
#>
#> ... (skipping 2 elements)
#>
#> [[5]]
#> [1] 9
#>
#> [[6]]
#> [1] 10
x <- as_flexseq(1:3)
fapply(x, function(el) el * 10)
#> Unnamed flexseq with 3 elements.
#>
#> Elements:
#>
#> [[1]]
#> [1] 10
#>
#> [[2]]
#> [1] 20
#>
#> [[3]]
#> [1] 30merge(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.
x <- flexseq("a", "b", "c", "d")
loop(for (el in x) {
print(el)
})
#> [1] "a"
#> [1] "b"
#> [1] "c"
#> [1] "d"For named flexseqs, loop() yields bare values (matching
peek_front()); use as.list() when you need
names alongside values.
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.
