tags:

views:

75

answers:

2

Is there an easy way to define a function which repeats itself when passed an argument?

For example, I've defined the following function

(defun swap-sign ()
  (interactive)
  (search-forward-regexp "[+-]")
  (if (equal (match-string 0) "-")
      (replace-match "+")
    (replace-match "-"))
  )

I'd like C-u swap-sign to call swap-sign four times.

I've tried

(defun swap-sign (&optional num)
  (interactive)
  (let ((counter 0)
 (num (if num (string-to-number num) 0)))
    (while (<= counter num)
      (search-forward-regexp "[+-]")
      (if (equal (match-string 0) "-")
   (replace-match "+")
 (replace-match "-"))           
      (setq counter (1+ counter)))))

but C-u swap-sign still only runs swap-sign (or perhaps more precisely, the body of the while-loop) once. I'm guessing it is because if num is not the right way to test if num is an empty string.

Am I on the right track, or is there a better/easier way to extend swap-sign?

+5  A: 
(defun swap-sign (arg)
  (interactive "p")
  (dotimes (i arg)
    (search-forward-regexp "[+-]")
    (if (equal (match-string 0) "-")
        (replace-match "+")
      (replace-match "-"))))

See the documentation of the interactive special form for more details: C-h finteractiveRET.

huaiyuan
@huaiyuan: Beautiful. Thank you so much.
unutbu
+4  A: 

You need to tell emacs to expect, and pass the parameter in, by adding a "p" as the parameter specification for interactive (M-x apropos interactive to get the documentation). Here I've made the minimal change to your code to get it to work - note, however, that you don't need the let/while to do the iteration, and the arg doesn't need to be optional.

(defun swap-sign (&optional num)
  (interactive "p")
  (let ((counter 1))
    (while (<= counter num)
      (search-forward-regexp "[+-]")
      (if (equal (match-string 0) "-")
      (replace-match "+")
    (replace-match "-"))           
      (setq counter (1+ counter)))))

Note that you don't need to convert the parameter from a string - using "p" tells emacs to do this for you.

Peter Hart
@Peter: Thanks for showing me how to fix the code with minimal changes.
unutbu
Thanks. @huaiyuan's solution is much cleaner than mine, though....
Peter Hart