When I execute the following Common Lisp program by calling (play), I get the error: Argument X is not a NUMBER: Guess
;;;; number-game.lisp
;;;;
;;;; Andrew Levenson
;;;; 10/25/2010
;;;;
;;;; Simple number guessing game. User has
;;;; five guesses to determine a number between
;;;; one and one hundred, inclusive (1-100).
;;; Set global variable for the target number:
(defparameter *target* nil)
;;; Set the iterator so we may check the number of guesses
(defparameter *number-of-guesses* 0)
;;; Welcome the user
(defun welcome-user ()
(format t "Welcome to the number guessing game!~%"))
;;; Prompt for a guess
(defun prompt-for-guess ()
(format t "Please enter your guess (1-100): ")
(finish-output nil) ; nil directs finish-output to standard IO
(check-guess 'read-guess))
;;; Read in a guess
(defun read-guess ()
(let ((guess (parse-integer(read-line *query-io*))))
(if (numberp guess) ; If true, return guess. Else, call prompt-for-guess
(progn
(setq *number-of-guesses* (+ *number-of-guesses* 1))
'guess)
(prompt-for-guess))))
;;; Check if the guess is higher than, lower than, or equal to, the target
(defun check-guess (fn)
(let ((guess (funcall fn)))
(if (= guess *target*)
(equal-to)
(if (> guess *target*)
(greater-than (guess))
(if (< guess *target*)
(less-than (guess)))))))
;;; If the guess is equal to the target, the game is over
(defun equal-to ()
(format t "Congratulations! You have guessed the target number, ~a!~%" *target*)
(y-or-n-p "Play again? [y/n] "))
;;; If the guess is greater than the target, inform the player.
(defun greater-than (guess)
(format t "Sorry, ~a is greater than the target.~%" guess)
(if (< *number-of-guesses* 6)
(prompt-for-guess)
(game-over)))
;;; If the guess is less than the target, inform the player.
(defun less-than (guess)
(format t "Sorry, ~a is less than the target.~%" guess)
(if (< *number-of-guesses* 6)
(prompt-for-guess)
(game-over)))
;;; If the player has run out of guesses, give them the option
;;; of playing the game again.
(defun game-over ()
(y-or-n-p "You have run out of guesses. Play again? [y/n] "))
;;; Play the game
(defun play ()
;; If it's their first time playing this session,
;; make sure to greet the user.
(unless (> *number-of-guesses* 0)
(welcome-user))
;; Reset their remaining guesses
(setq *number-of-guesses* 0)
;; Set the target value
(setq *target*
;; Random can return float values,
;; so we must round the result to get
;; an integer value.
(round
;; Add one to the result, because
;; (random 100) yields a number between
;; 0 and 99, whereas we want a number
;; from 1 to 100 inclusive.
(+ (random 100) 1)))
(if (equal (prompt-for-guess) "y")
(play)
(quit)))
What am I doing wrong that guess is not a number value in check-guess?
Your read-guess function returns the symbol GUESS by using 'guess rather than the value of guess (ie without the single quote).
I think you also have other issues, for example (greater-than (guess)) evaluates guess as a function. You will need to fix these also.
Related
I am doing a lot of embedded C programming right now, which means that I am writing things like this all the time:
(ioe_extra_A & 0xE7)
It would be super useful, if when put my cursor on the 0xE7, emacs would display "0b1110 0111" in the status bar or mini-buffer, so I could check that my mask is what I meant it to be.
Typically, no matter what it is I want emacs to do, 10 minutes of Googling will turn up the answer, but for this one, I have exhausted my searching skills and still not turned up an answer.
Thanks ahead of time.
This seems to work:
(defvar my-hex-idle-timer nil)
(defun my-hex-idle-status-on ()
(interactive)
(when (timerp my-hex-idle-timer)
(cancel-timer my-hex-idle-timer))
(setq my-hex-idle-timer (run-with-idle-timer 1 t 'my-hex-idle-status)))
(defun my-hex-idle-status-off ()
(interactive)
(when (timerp my-hex-idle-timer)
(cancel-timer my-hex-idle-timer)
(setq my-hex-idle-timer nil)))
(defun int-to-binary-string (i)
"convert an integer into it's binary representation in string format
By Trey Jackson, from https://stackoverflow.com/a/20577329/."
(let ((res ""))
(while (not (= i 0))
(setq res (concat (if (= 1 (logand i 1)) "1" "0") res))
(setq i (lsh i -1)))
(if (string= res "")
(setq res "0"))
res))
(defun my-hex-idle-status ()
(let ((word (thing-at-point 'word)))
(when (string-prefix-p "0x" word)
(let ((num (ignore-errors (string-to-number (substring word 2) 16))))
(message "In binary: %s" (int-to-binary-string num))))))
Type M-x my-hex-idle-status-on to turn it on.
As noted, thanks to Trey Jackson for int-to-binary-string.
I'm writing a simple mode for a Lisp-like language, and am having trouble setting up indentation. I've been following the emacswiki mode tutorial.
However, I can't figure out how to adapt their example indentation to my needs because they don't do any form of counting.
Basically, I just need to add 2 spaces to my indentation count every time I see a { or (, even if there are multiple on the same line, and subtract 2 spaces when I see closures of the above. I'm new to elisp; how can I adapt their example to count braces and brackets?
For convenience, here is the code they are using (for a non-bracket language):
(defun wpdl-indent-line ()
"Indent current line as WPDL code"
(interactive)
(beginning-of-line)
(if (bobp) ; Check for rule 1
(indent-line-to 0)
(let ((not-indented t) cur-indent)
(if (looking-at "^[ \t]*END_") ; Check for rule 2
(progn
(save-excursion
(forward-line -1)
(setq cur-indent (- (current-indentation) default-tab-width)))
(if (< cur-indent 0)
(setq cur-indent 0)))
(save-excursion
(while not-indented
(forward-line -1)
(if (looking-at "^[ \t]*END_") ; Check for rule 3
(progn
(setq cur-indent (current-indentation))
(setq not-indented nil))
; Check for rule 4
(if (looking-at "^[ \t]*\\(PARTICIPANT\\|MODEL\\|APPLICATION\\|WORKFLOW\\|ACTIVITY\\|DATA\\|TOOL_LIST\\|TRANSITION\\)")
(progn
(setq cur-indent (+ (current-indentation) default-tab-width))
(setq not-indented nil))
(if (bobp) ; Check for rule 5
(setq not-indented nil)))))))
(if cur-indent
(indent-line-to cur-indent)
(indent-line-to 0))))) ; If we didn't see an indentation hint, then allow no indentation
How can I just implement lisp-like indentation (but also with curly braces)?
If you want something simple for a Lisp-style language, I suggest you start with (syntax-ppss) which returns the "parsing state" at point. The first element of that state is the current paren-nesting depth. While I used the word "paren", this doesn't really count parens but counts those chars which the syntax-table defines as paren-like, so if you set your syntax-table such that { and } are declared as paren-like, then those will also be counted.
So you could start with something like
(defun foo-indent-function ()
(save-excursion
(beginning-of-line)
(indent-line-to (* 2 (car (syntax-ppss))))))
Do not define this as interactive, since the way to use it is by adding
(set (make-local-variable 'indent-line-function) #'foo-indent-function)
in your major-mode function.
But maybe a better option is to simply do:
(require 'smie)
...
(define-derived-mode foo-mode "Foo"
...
(smie-setup nil #'ignore)
...)
This will use an indentation step of 4 (configured in smie-indent-basic).
I don't like how plists are indented in Elisp.
;; current desired Python (for comparison)
;; '(a 1 '(a 1 {'a': 1,
;; b 2 b 2 'b': 2,
;; c 3) c 3) 'c': 3}
Tried on M-x emacs-version 24.3.1, ran emacs -Q, typed the plist and pressed C-x h C-M-\.
This indentation makes sense when it isn't a list:
(mapcar (lambda (x) (x + 1))
'(1 2 3 4))
How do I change formatting settings so that only plists (or, if that's impossible, all quoted lists) have the desired rectangular indentation, but indentation of everything else stays the same? I need this stored locally in an .el file, so that when I edit this file, it is indented as desired, but this behavior doesn't end up anywhere else.
Found it:
(setq lisp-indent-function 'common-lisp-indent-function)
Here's a sample file:
(setq x '(a 1
b 2
c 3))
;;; Local Variables:
;;; lisp-indent-function: common-lisp-indent-function
;;; End:
I'll just dump my whole indentation config here:
(setq lisp-indent-function 'common-lisp-indent-function)
(put 'cl-flet 'common-lisp-indent-function
(get 'flet 'common-lisp-indent-function))
(put 'cl-labels 'common-lisp-indent-function
(get 'labels 'common-lisp-indent-function))
(put 'if 'common-lisp-indent-function 2)
(put 'dotimes-protect 'common-lisp-indent-function
(get 'when 'common-lisp-indent-function))
You can fix this (in my opinion) bug by overriding lisp-indent-function. The original source of the hack was this Github Gist, which was referenced with some more explanation from this Emacs Stack Exchange answer.
However, I was very uncomfortable overriding a core function like this. For one, it's very opaque—how is a reader supposed to tell what is changed? And worse—what if the official definition of lisp-indent-function changed in the future? How would I know that I needed to update my hack?
As a response, I created the library el-patch, which is specifically designed to address this problem. After installing the package, you can override lisp-indent-function as follows:
(el-patch-defun lisp-indent-function (indent-point state)
"This function is the normal value of the variable `lisp-indent-function'.
The function `calculate-lisp-indent' calls this to determine
if the arguments of a Lisp function call should be indented specially.
INDENT-POINT is the position at which the line being indented begins.
Point is located at the point to indent under (for default indentation);
STATE is the `parse-partial-sexp' state for that position.
If the current line is in a call to a Lisp function that has a non-nil
property `lisp-indent-function' (or the deprecated `lisp-indent-hook'),
it specifies how to indent. The property value can be:
* `defun', meaning indent `defun'-style
(this is also the case if there is no property and the function
has a name that begins with \"def\", and three or more arguments);
* an integer N, meaning indent the first N arguments specially
(like ordinary function arguments), and then indent any further
arguments like a body;
* a function to call that returns the indentation (or nil).
`lisp-indent-function' calls this function with the same two arguments
that it itself received.
This function returns either the indentation to use, or nil if the
Lisp function does not specify a special indentation."
(el-patch-let (($cond (and (elt state 2)
(el-patch-wrap 1 1
(or (not (looking-at "\\sw\\|\\s_"))
(looking-at ":")))))
($then (progn
(if (not (> (save-excursion (forward-line 1) (point))
calculate-lisp-indent-last-sexp))
(progn (goto-char calculate-lisp-indent-last-sexp)
(beginning-of-line)
(parse-partial-sexp (point)
calculate-lisp-indent-last-sexp 0 t)))
;; Indent under the list or under the first sexp on the same
;; line as calculate-lisp-indent-last-sexp. Note that first
;; thing on that line has to be complete sexp since we are
;; inside the innermost containing sexp.
(backward-prefix-chars)
(current-column)))
($else (let ((function (buffer-substring (point)
(progn (forward-sexp 1) (point))))
method)
(setq method (or (function-get (intern-soft function)
'lisp-indent-function)
(get (intern-soft function) 'lisp-indent-hook)))
(cond ((or (eq method 'defun)
(and (null method)
(> (length function) 3)
(string-match "\\`def" function)))
(lisp-indent-defform state indent-point))
((integerp method)
(lisp-indent-specform method state
indent-point normal-indent))
(method
(funcall method indent-point state))))))
(let ((normal-indent (current-column))
(el-patch-add
(orig-point (point))))
(goto-char (1+ (elt state 1)))
(parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
(el-patch-swap
(if $cond
;; car of form doesn't seem to be a symbol
$then
$else)
(cond
;; car of form doesn't seem to be a symbol, or is a keyword
($cond $then)
((and (save-excursion
(goto-char indent-point)
(skip-syntax-forward " ")
(not (looking-at ":")))
(save-excursion
(goto-char orig-point)
(looking-at ":")))
(save-excursion
(goto-char (+ 2 (elt state 1)))
(current-column)))
(t $else))))))
Here is another less heavyweight solution, based on emacsql-fix-vector-indentation. An advice around calculate-lisp-indent is sufficient.
This only works for plists that use keywords as keys, but that covers a majority of plists. To make this work on quoted lists instead, you could change the looking-at regexp to detect the ' or "`", but that will not cover, say, a nested list.
This can further be packaged up into a minor mode if there is a need to turn it off.
(defun my/inside-plist? ()
"Is point situated inside a plist?
We determine a plist to be a list that starts with a keyword."
(let ((start (point)))
(save-excursion
(beginning-of-defun)
(let ((sexp (nth 1 (parse-partial-sexp (point) start))))
(when sexp
(setf (point) sexp)
(looking-at (rx "(" (* (syntax whitespace)) ":")))))))
(define-advice calculate-lisp-indent (:around (func &rest args)
plist)
(if (save-excursion
(beginning-of-line)
(my/inside-plist?))
(let ((lisp-indent-offset 1))
(apply func args))
(apply func args)))
I feel a bit silly for asking this question, but I feel like my code is as inefficient as it can be. I think I do not have the logic going on too well here.
Basically, I would like to have some different things happen on subsequently running the same commands.
My idea was to have a (cond ), in which for each case I have a test whether the command used before is the same AND the value of a variable which is set according to how many times it was pressed.
I also feel like I am not getting the title/tags correctly in this case, so feel free to edit.
((and (eq last-repeatable-command 'thecommand)
(= varcounter 1))
(message "second time called")
(setq varcounter 2))
When it is pressed again, the next clause would fire.
While the code below works, I believe this could be done way more efficiently, and I hope someone can give directions on how to approach this problem.
Long code example:
(defun incremental-insert-o ()
(interactive)
; init if not bound
(when (not (boundp 'iivar)) (setq iivar 0))
(cond
((and (eq last-repeatable-command 'incremental-insert-o)
(= iivar 1))
(insert "o o ")
(setq iivar 2))
((and (eq last-repeatable-command 'incremental-insert-o)
(= iivar 2))
(insert "o o o ")
(setq iivar 3))
((and (eq last-repeatable-command 'incremental-insert-o)
(= iivar 3))
(insert "o o o "))
(t
(insert "o ")
(setq iivar 1)))
)
(global-set-key [f8] 'incremental-insert-o)
Now, you're asking for more efficient code. There are a few things you could mean by this. You could mean that you want code that executes faster. How slow is the code now? When I run it on my Emacs, it's instant. Given that this code, by definition, is called from a buttonpress, it doesn't have to be super fast. Your code is more than fast enough for its use case, so I wouldn't worry about making it any faster. It also doesn't use memory: if you call it n times, it'll still only use enough memory to store one integer: this algorithm is O(1). Sounds good to me.
You could also mean "write this in fewer lines". This will also make the code less error-prone, and easier to understand. That's certainly a reasonable goal. Your code isn't awful to begin with, so it's not a necessity, but nor is it a bad idea. There are a few modifications we could make to your function. You could drop the entire third clause of your cond, and let the (= iivar 2) case be the final one, eliminating the need to set iivar to 3 there. Well, that's better already.
But wait, the function calls (eq last-repeatable-command 'incremental-insert-o) up to three times! That's a lot. Let me try to rewrite it! First, let's start with a base function definition, with an interactive call, as you have:
(defun incremental-insert-o ()
(interactive))
Now, I'm going to restructure things from your code. First, let's see if we can keep track of iivar correctly. I'm going to rename that variable to incremental-insert-o-consecutive, for readability, and because Emacs Lisp has a single namespace, so anything else using a variable named iivar will read and write to the same place your code's looking at:
(defun incremental-insert-o ()
(interactive)
(if (eq last-repeatable-command 'incremental-insert-o)
(setq incremental-insert-o-consecutive
(1+ incremental-insert-o-consecutive))
(setq incremental-insert-o-consecutive
1)))
Is that working? I'll bind it to [F8] as you did: (global-set-key [f8] 'incremental-insert-o). Now, hit [F8] to run it, but it doesn't tell you what the return value is. Let's change the function slightly to test it:
(defun incremental-insert-o ()
(interactive)
(if (eq last-repeatable-command 'incremental-insert-o)
(setq incremental-insert-o-consecutive
(1+ incremental-insert-o-consecutive))
(setq incremental-insert-o-consecutive
1))
(message "incremental-insert-o-consecutive is currently %s" incremental-insert-o-consecutive))
Hit [F8] a few times to make sure it works, and it does! It starts at 1, increases by 1 each consecutive time it's called, and resets when you do something else. Now, we just need to print out the right message. What do we want to print? Well, the first time you call the function, print out one "o ", then the second time, print out "o o ", then the third and all other times, print "o o o ". Note that printing the second string is just printing the first string twice, and the third string is printing the first string three times:
(defun incremental-insert-o ()
(interactive)
(if (eq last-repeatable-command 'incremental-insert-o)
(setq incremental-insert-o-consecutive
(1+ incremental-insert-o-consecutive))
(setq incremental-insert-o-consecutive
1))
(dotimes (i incremental-insert-o-consecutive)
(insert "o ")))
This is almost right! It does the right thing for times 1 through 3, but doesn't cap off at inserting "o o o "; it goes on to print "o o o o ", etc. So we just need to cap off the limit of repeats at 3:
(defun incremental-insert-o ()
(interactive)
(if (eq last-repeatable-command 'incremental-insert-o)
(setq incremental-insert-o-consecutive
(1+ incremental-insert-o-consecutive))
(setq incremental-insert-o-consecutive
1))
(dotimes (i (min incremental-insert-o-consecutive
3))
(insert "o ")))
Now, this seems to do exactly what you want. Let's look at the changes from the original function. This counts the number of repeats beyond 3. But the output behavior is the same, so I don't think this matters, and it seems nicer to keep the actual count of repeats. It will break if you ever overflow the integer, but that seems unlikely. Emacs guarantees at least 536870911 as MAXINT. So let's call that a day. We did get the code shorter, and have no repeated parts. I think that makes it more readable.
Here's something I could think of, however, take it with a grain of salt, because it may be overly complex, and you don't want to bring this much complexity into what you do:
(defstruct command-state
action next-state)
(defmacro define-action-states (name condition &rest actions)
(labels ((%make-command-state
(action name)
`(make-command-state :action (lambda () ,action))))
`(let ((head ,(%make-command-state (car actions) name)))
(defvar ,name nil)
(setq ,name head)
,#(loop for action in (cdr actions)
collect
`(setf (command-state-next-state ,name)
,(%make-command-state action name)
,name (command-state-next-state ,name)))
(setf (command-state-next-state ,name) head
,name head)
(defun ,(intern (concat (symbol-name name) "-command")) ()
(when ,condition
(unwind-protect
(funcall (command-state-action ,name))
(setq ,name (command-state-next-state ,name))))))))
(define-action-states print-names (= 1 1)
(message "first state")
(message "second state")
(message "third state")
(message "fourth state"))
(print-names-command)
;; will print messages looping through them,
;; each time you call it
I've made it to use a struct, so that you could add more conditions, independent of the state itself, for example, but mostly so the names would be more self-explanatory.
Also, probably, that's not the place you should really care about efficiency - so far your fingers cannot outrun the eLisp interpreter, it's all good ;)
Here's something I did to your code to possibly improve it a bit (now the worst case scenario will only check 5 conditions instead of 6 :)
(defun smart-killer ()
(interactive)
(let* ((properties (symbol-plist 'smart-killer))
(counter (plist-get properties :counter)))
(if (region-active-p)
(kill-region (region-beginning) (region-end))
(if (eq last-repeatable-command 'smart-killer)
(if (> counter 3)
(message "Kill ring is already filled with paragraph.")
(if (> counter 2)
(progn
(yank)
(kill-new "")
(mark-paragraph -1)
(kill-region (region-beginning) (region-end)))
(if (> counter 1)
(kill-region (point) (line-beginning-position))
(kill-line))))
(when (not (looking-at "\\<\\|\\>")) (backward-word)) ; begin/end of word
(kill-word 1))
(plist-put properties :counter (mod (1+ counter) 5)))))
(put 'smart-killer :counter 0)
This is what I came up with in the end:
(defun smart-killer ()
(interactive)
(cond
; [1] If region active, kill region
((region-active-p)
(kill-region (region-beginning) (region-end)))
; [2] If this command was last called, check how many times before it ran
((eq last-repeatable-command 'smart-killer)
(cond
; [2a]
((= sm-killer 1)
(kill-line))
; [2b]
((= sm-killer 2)
(kill-region (point) (line-beginning-position)))
; [2c]
((= sm-killer 3)
(yank)
(kill-new "")
(mark-paragraph -1)
(kill-region (region-beginning) (region-end)))
; [2d]
((= sm-killer 4)
(message "Kill ring is already filled with paragraph.")))
(incf sm-killer))
; [3]
(t
(when (not (looking-at "\\<\\|\\>")) (backward-word)) ; begin/end of word
(kill-word 1)
(setq sm-killer 1)))
)
I programmed some months ago some code with a lot of if statements. If region-active-p, if beginning-of-line, those kind of things.
Having learned about the cond lisp, I was wondering if I could improve my code a lot.
The problem is that this cond is only doing things when "true" as far as I see it, while I actually need the move back-to-indentation in between these checks.
In order to properly skip the last clause, I even have to set variable values.
(defun uncomment-mode-specific ()
"Uncomment region OR uncomment beginning of line comment OR uncomment end"
(interactive)
(let ((scvar 0) (scskipvar 0))
(save-excursion
(if (region-active-p)
(progn (uncomment-region (region-beginning) (region-end))
(setq scskipvar 1))
(back-to-indentation)) ; this is that "else" part that doesn't fit in cond
(while (string= (byte-to-string (following-char)) comment-start)
(delete-char 1)
(setq scskipvar 1))
(indent-for-tab-command)
(when (= scskipvar 0)
(search-forward comment-start nil t)
(backward-char 1)
(kill-line))
)))
)
So basically my question is, I would kind of like to have some consequences of not giving "true" to a clause, before the check of another clause. Is this possible? If not, what would be the best thing to do?
EDIT: Since we are using this as the example case for a solution, I wrote it down so it is easier to understand.
If region is active, remove comments from region. If not, move point to intendation.
For as long as the following character is a comment character, delete it. Afterwards, indent this line.
If it didn't do any of the above, search forward for a comment character, and kill that line.
(defun delete-on-this-line (regex)
(replace-regexp regex "" nil (line-beginning-position) (line-end-position)))
(defun delete-leading-comment-chars ()
(delete-on-this-line (eval `(rx bol (* space) (group (+ ,comment-start))))))
(defun delete-trailing-comment-chars ()
(delete-on-this-line (eval `(rx (group (+ ,comment-end)) (* space) eol))))
(defun delete-trailing-comment ()
(delete-on-this-line (eval `(rx (group (+ ,comment-start) (* anything) eol)))))
(defun uncomment-dwim ()
(interactive)
(save-excursion
(if (region-active-p)
(uncomment-region (region-beginning) (region-end))
(or (delete-leading-comment-chars)
(delete-trailing-comment-chars)
(delete-trailing-comment)))))
Edit: A little explanation:
It's a lot easier to do regex replacements than manage loops to do deletion, so that gets rid of the state. And the steps are all mutually exclusive, so you can just use or for each option.
The rx macro is a little DSL that compiles down to valid regexes, and it's also amenable to lispy syntax transforms, so I can dynamically build a regex using the comment chars for the current mode.
(defmacro fcond (&rest body)
(labels ((%substitute-last-or-fail
(new old seq)
(loop for elt on seq
nconc
(if (eql (car elt) old)
(when (cdr elt)
(error "`%S' must be the last experssion in the clause"
(car elt)))
(list new)
(list (car elt))))))
(loop with matched = (gensym)
with catcher = (gensym)
for (head . rest) in body
collect
`(when (or ,head ,matched)
(setq ,matched t)
,#(%substitute-last-or-fail `(throw ',catcher nil) 'return rest))
into clauses
finally
(return `(let (,matched) (catch ',catcher ,#clauses))))))
(macroexpand '(fcond
((= 1 2) (message "1 = 2"))
((= 1 1) (message "1 = 1"))
((= 1 3) (message "1 = 3") return)
((= 1 4) (message "1 = 4"))))
(let (G36434)
(catch (quote G36435)
(when (or (= 1 2) G36434)
(setq G36434 t)
(message "1 = 2"))
(when (or (= 1 1) G36434)
(setq G36434 t)
(message "1 = 1"))
(when (or (= 1 3) G36434)
(setq G36434 t)
(message "1 = 3")
(throw (quote G36435) nil))
(when (or (= 1 4) G36434)
(setq G36434 t)
(message "1 = 4"))))
Here's something quick to do, what I think you may be after, i.e. something that would mimic the behaviour switch in C.
The idea is that all clauses are tested sequentially for equality, and if one matches, then all following clauses are executed, until the return keyword (it would be break in C, but Lisp uses return for the similar purpose in the loop, so I thought that return would be better). The code above thus will print:
1 = 1
1 = 3
Technically, this is not how switch works in C, but it will produce the same effect.
One thing I did here for simplicity, which you want to avoid / solve differently - the use of return keyword, you probably want to impose stricter rules on how it should be searched for.
cond
Cond evaluates a series of conditions in a list, each item in a list can be a condition, and then executable instructions.
The example in the Emacs Lisp manual is adequate to demonstrate how it works, I've annotated it here to help you understand how it works.
(cond ((numberp x) x) ;; is x a number? return x
((stringp x) x) ;; is x a string? return x
((bufferp x) ;; is x a buffer?
(setq temporary-hack x) ;; set temporary-hack to buffer x
(buffer-name x)) ;; return the buffer-name for buffer x
((symbolp x) (symbol-value x))) ;; is x a symbol? return the value of x
Each part of the condition can be evaluated any way you like, the fact x above is in each condition is coincidental.
For example:
(cond ((eq 1 2) "Omg equality borked!") ;; Will never be true
(t "default")) ;; always true
So comparisons with switch are a bit limited, it's essentially a list of if statements, that executes/returns the first true condition's body list.
Hopefully this helps you understand cond a bit better.
(cond (condition body ... ) ;; execute body of 1st passing
(condition body ... ) ;; condition and return result
(condition body ... ) ;; of the final evaluation.
;; etc
)
OR
You can do things similar to switch with OR, depending on how you structure the code.
This isn't functional style, because it relies on side-effects to do what you want, then returns a boolean value for flow control, here's an example in pseudo lisp.
(or)
(or
(lambda() (do something)
(evaluate t or nil) ; nil to continue; t to quit.
)
(lambda() (do something)
(evaluate t or nil) ; nil to continue; t to quit.
)
(lambda() (do something)
(evaluate t or nil) ; nil to continue; t to quit.
)
(lambda() (do something)
(evaluate t or nil) ; nil to continue; t to quit.
)
)
Here's working example of a switch like structure using or
(or
(when (= 1 1)
(progn
(insert "hello\n")
nil))
(when (= 1 2) ;; condition fails.
(progn
(insert "hello\n")
nil)) ;; returns false (nil)
(when (= 1 1)
(progn
(insert "hello\n")
t)) ;; returns true, so we bail.
(when (= 1 1)
(progn
(insert "hello\n")
nil))
)
Inserts :
hello
hello
(and)
The and operator (not just in Lisp) is also very useful, instead of evaluating everything until true, it evaluates conditions that are true, until a false is evaluated.
Both or & and can be used to build useful logic trees.
This is how I did it now according to Chris' idea that breaking it down into seperate functions would make it easier.
EDIT: Now also applied the or knowledge gained in this thread gained from Slomojo (no more variables!)
(defun sc-uncomment ()
(interactive)
(or
(if (region-active-p)
(uncomment-region (region-beginning) (region-end))
(back-to-indentation)
nil)
(if (string= (byte-to-string (following-char)) comment-start)
(sc-check-start)
(sc-end))))
(defun sc-check-start ()
(interactive)
(while (string= (byte-to-string (following-char)) comment-start)
(delete-char 1))
)
(defun sc-end ()
(interactive)
(search-forward comment-start nil t)
(backward-char 1)
(kill-line))
)