Yes. Functions ring-next
and ring-previous
do what you're asking, forward and backward, respectively.
It may not be obvious, but invoking ring-next
on the last ring element cycles/rotates to the first element: wrap-around. Similarly, for ring-previous
on the first element: it rotates to the last element.
I've filed Emacs bug #42430, to ask that the Elisp manual mention these functions. Thx.
To respond to your comment:
simple indexed access modulo the length of the structure isn't what
I'd call "rotation." To me, "you can rotate a ring" sounds like a
mutating action that takes a distance N and has the effect that every
element previously at index I is now at index I - N (modulo the length
of the ring, of course). For example, given a ring with elements (1 2
3 4 5), I'd expect to have a function like (ring-rotate my-ring 2) and
have the ring now contain (3 4 5 1 2).
No, that functionality is not offered as such, nor is it really needed, in general. It's not what was meant by "rotating" a ring. Perhaps that word isn't the best one for what is meant.
A ring in Elisp is not a list whose order matters. It's an instance of an abstract data type, if you like. This is true of kill-ring
, search-ring
, etc.
You have access to the ring elements by ring position, not implementation-list position, and you can change the position of a given element if you need to (as you suggested, with insertion and deletion). But the more typical operations are insertion at the head, deletion of a given element, accessing an element by its position/index, and accessing the next/previous ring element.
Something like this seems to be what you're looking for:
(defun ring-rotate (ring &optional n)
"Rotate RING N places.
N defaults to 1.
Moves ring element 2 to position 1, 3 to 2, 4 to 3, etc.
Moves element 1 to the end of the ring."
(setq n (or n 1))
(let ((rng ring))
(dotimes (ii n rng)
(ring-insert rng (ring-remove rng)))))
Update: FYI, Emacs bug #42430 was closed by the Emacs maintainer, with this comment:
The manual says:
For yanking, one entry in the kill ring is designated the front of
the ring. Some yank commands rotate the ring by designating a different
element as the front. But this virtual rotation doesn’t change the list
itself—the most recent entry always comes first in the list.
That's all. I don't see any reason to expand this description of a
"virtual rotation", since that would require bringing many internal
details into the ELisp manual, with no apparent gain.