Is it possible to break out of a dolist
loop? if not, are there alternatives to dolist
that don't involve a while loop and indexing a list?

- 75,699
- 9
- 109
- 225

- 8,375
- 1
- 28
- 105
-
Pitingly, the `while` loop is excluded. IMHO it is the only pre-defined loop avoiding non-local exit when breaking out of a list iteration. It can be head or tail controlled or anything in between. – Tobias Nov 13 '19 at 07:31
-
BTW you added `The catch statement evaluates to the value thrown (x in this case), or nil if no value is thrown.` to [lawlist's answer](https://emacs.stackexchange.com/a/53708/2370). How do you throw without passing a value? The `VALUE` arg of `throw` is obligatory. Maybe, you mean `cl-return` where it is optional. – Tobias Nov 13 '19 at 07:33
-
@Tobias it was poor wording, now corrected. – ideasman42 Nov 13 '19 at 08:28
-
I've expanded it a bit further to avoid any misunderstandings. – Tobias Nov 13 '19 at 08:54
2 Answers
One option is to use catch
/ throw
:
https://www.gnu.org/software/emacs/manual/html_node/elisp/Catch-and-Throw.html
(catch 'foo
(dolist (x '("a" "b" "c" "d" "e" "f" "g"))
(when (equal x "d")
(throw 'foo x))))
The catch statement evaluates to the value thrown (x
in this case), or the value of the last body form if the throw
function never executes. In the case at hand the dolist
form is the last entry in the body. The optional RESULT
argument of (dolist (VAR LIST [RESULT]) BODY...)
is not specified. Therefore, it evaluates to nil.
As pointed out by @Drew and @Tobias in comments below, the dolist
function that is available without loading the cl
library is defined in subr.el
. Once the cl
library is loaded, however, describing the function with C-h f
reflects that dolist
has been modified as follows: :around advice: ‘cl--wrap-in-nil-block’
. The function return
(which is an alias for cl-return
that is defined in the cl
library) can be used to break the dolist
loop. To load the cl
library, begin with (require 'cl)
:
(dolist (x '("a" "b" "c" "d" "e" "f" "g"))
(when (equal x "d")
(return x)))
-
2+1. Maybe make clearer that without loading library `cl` the version of `dolist` is different (not an alias for `cl-dolist`). You can use `return` (`cl-return`) only with the version of `dolist` provided by library `cl` (it is an alias for `cl-dolist`). You more or less say that, but it could be clearer - there are two different `dolist`s, and only one lets you use `return`. – Drew Nov 13 '19 at 05:05
-
1@Drew `dolist` is never an alias for `cl-dolist`. `cl.el` rather wraps `dolist` into a nil-block by an around advice and `(cl-return val)` is essentially `(throw nil val)`. So there is not much difference between the two solutions of lawlist. The second solution is just more elegant if cl is available. – Tobias Nov 13 '19 at 05:55
-
1Do not require `cl`. That is depreciated. Better require `cl-macs` or `cl-lib` and use `cl-dolist` and `cl-return` instead of `dolist` and `return`. Maybe, explicitly state that and give both alternative solutions. – Tobias Nov 13 '19 at 06:03
-
@Tobias -- I do remember reading a portion of a thread on the Emacs Devel mailing list suggesting that the `cl` library be moved over to the `obsolete` directory of the Lisp source code, but I did not follow that thread to its conclusion. I will make a note to find that thread and read-up on it ... – lawlist Nov 13 '19 at 06:11
-
1Citation from the [cl info file](https://www.gnu.org/software/emacs/manual/html_node/cl/Organization.html): "Since the old `cl.el` does not use a clean namespace, Emacs has a policy that packages distributed with Emacs must not load `cl` at run time. (It is ok for them to load `cl` at compile time, with `eval-when-compile`, and use the macros it provides.) There is no such restriction on the use of `cl-lib`. New code should use `cl-lib` rather than `cl`." – Tobias Nov 13 '19 at 06:33
-
1@lawlist here is the thread about cl.el = declaring obsolete: http://emacs.1067599.n8.nabble.com/Declaring-cl-el-obsolete-td484852.html – Hubisan Nov 13 '19 at 09:23
-
1@tobias: Yes, that's what I meant - advice-wrapped with `nil` block (not aliased). My point was to make clear that they're different. unadvised `dolist` from `subr.el` does not support `(cl-)return`, as there is no CL block to return to (and `(cl-)return` is undefined). IMO, this would be a poor reason, on its own, to load `cl-macs` or `cl-lib`. (I personally find the `catch`-`throw` idiom for breaking out of loops etc. very clear - the "block" is explicit/visible, and the tag is essentially the block name.) – Drew Nov 13 '19 at 15:09
The Common Lisp Cookbook has an extensive section on loops. Among other things, as a substitute for dolist you could try a "for" loop and use "when" to exit. For example:
(require 'cl-lib)
(cl-loop for i in '(123 3 22 1 309)
when (< i 2)
return -500
do (print i))
... output ...
123
3
22
-500