5

I have little experience with CL/ELisp and don't know any function that iterate list and apply another function until get non-nil value, which is returned from loop, leaving rest list unprocessed.

It like lazy mapcar or merged or with apply.

For example I like fold while loop into more declarative code in:

(defvar autobm-try-thingatpt-things
  '(symbol url email))

(defun autobm-try-thingatpt ()
  (let ((things autobm-try-thingatpt-things) name)
    (while (and things (not name))
      (setq name (thing-at-point (car things)))
      (setq things (cdr things)))
    name))

UPDATE With answer of I rewrote code to:

(require 'cl-lib)

(defun autobm-try-thingatpt ()
  (cl-some #'thing-at-point autobm-try-thingatpt-things))

It look elegant! Only question to experienced elispers why cl-extra does not provide itself? So I must use (load "cl-extra") instead of (require 'cl-extra)...

  • 1
    In the case this question and your another question are related, which-func does this job for you. You simply provide it a list of functions you want to use in which-func-functions var and it won't execute the remaining functions in the list once a function returns a non-nil value (functions to the left in the list are executed first). – Kaushal Modi Apr 20 '15 at 19:28
  • Yes. Currently I found 2 sources of func name for prog-mode but like process any registered possible methods in specified list order until first success - in that way it is possible to set preferred method firstly and fall back to another. semantic for example also provide type of declaration so I can format it as var: ... or fn: ..., which isn't possible with wich-func.... – gavenkoa Apr 21 '15 at 15:40
  • 1
    You're supposed to just (require 'cl-lib) rather than loading a specific file (also you shouldn't be using eval-when-compile because you need the function at runtime, not compile time) – npostavs Apr 21 '15 at 20:42
  • @npostavs Thanks for valuable notes! I search for provide in cl-extra but didn't find it. After all cl-lib do (load "cl-loaddefs") which has lines like (autoload 'cl-some "cl-extra" ...) – gavenkoa Apr 21 '15 at 20:45
7

You're looking for cl-some, which is found in 'cl-extra. It takes a predicate1 and a sequence, and returns the first non-nil call of the predicate called on each element of the sequence, or nil if that function returns nil on all elements of the sequence.

For simplicity, I'm going to use a helper function for this example

(defun if-negative-return-absolute-value (num)
  "If NUM is negative, return its absolute value. Otherwise, return nil."
  (when (< num 0)
    (abs num)))


(require 'cl-extra)

(cl-some  #'if-negative-return-absolute-value '(1 2 3))
=> nil    ;; if-negative-return-absolute-value returns nil on all elements

(cl-some  #'if-negative-return-absolute-value '(1 2 3 -4 -5))
=> 4      ;; the first non-nil result of if-negative-return-absolute-value

Your code can now be rewritten to be a lot simpler:

(defun autobm-try-thingatpt ()
  (cl-some #'thing-at-point autobm-try-thingatpt-things))

You may want to switch around the order of things in autobm-try-thingatpt-things; having the order be '(url email symbol) seems to be correct, as if point is inside a url or email, it'll still find a symbol, and urls can contain things that look like email addresses.

Thanks to @npostavs for pointing out the proper cl function to use here.

[1] A predicate is just a function used for the "truthiness" of its result -- whether it's nil or not.

  • 2
    I think they might have been asking for cl-some. – npostavs Apr 20 '15 at 16:04
  • @npostavs Thanks, that's the function I couldn't find. I updated the answer to use it. – zck Apr 20 '15 at 19:22
  • Wow! Exactly what I want, after you answer I quickly found Common Lisp functions: EVERY, SOME, NOTEVERY, NOTANY lispworks.com/documentation/HyperSpec/Body/f_everyc.htm for those tasks, for that reason I ask question here. Thanks +1 – gavenkoa Apr 21 '15 at 15:48
3
(defun autobm-try-thingatpt (fn)
  (let ((result  nil))
    (catch 'here
      (dolist (thg  autobm-try-thingatpt-things)
        (when (setq result  (funcall fn thg))
          (throw 'here result))))
    result))

That assumes that the result you want returned is the value of applying the function to the list element. If instead you wanted the (first) list element for which applying the function to it returns non-nil, then set result to the thg before throwing it.

  • To shorter code is last result required? Is it usual to do let inside catch? – gavenkoa Apr 20 '15 at 12:56
  • 1
    1. Explicitly returning result is clearer. It is not great style to rely on the return value of dolist (which will be the return value of the catch if there is nothing thrown) - it is less clear to a human reader what is going on. 2. The catch and throw are there to stop iterating when you find what you are looking for. – Drew Apr 20 '15 at 15:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.