1

几个月前我编写了一些带有很多if语句的代码。如果region-active-p,如果beginning-of-line,诸如此类的事情。

在了解了condlisp 之后,我想知道我是否可以大大改进我的代码。

问题是,据我所知,这cond只是在“真实”时才做的事情,而我实际上需要back-to-indentation在这些检查之间移动。

为了正确跳过最后一个子句,我什至必须设置变量值。

(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))
    )))
)

所以基本上我的问题是,在检查另一个子句之前,我有点想对一个子句不“真实”给出一些后果。这可能吗?如果没有,最好的办法是什么?

编辑:由于我们将此作为解决方案的示例案例,因此我将其写下来以便更容易理解。

如果区域处于活动状态,请从区域中删除评论。如果没有,将点移至意向。

只要后面的字符是注释字符,就删除它。然后,缩进这一行。

如果它没有执行上述任何操作,请向前搜索注释字符,然后终止该行。

4

4 回答 4

4
(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"))))

这里有一些快速做的事情,我认为你可能会追求,即模仿switchC 中的行为的东西。

这个想法是顺序测试所有子句的相等性,如果一个匹配,则执行所有后续子句,直到return关键字(它会break在 C 中,但 Lispreturn在循环中用于类似目的,所以我认为这return会会更好)。因此,上面的代码将打印:

1 = 1
1 = 3

从技术上讲,这不是switchC 中的工作方式,但它会产生相同的效果。

为了简单起见,我在这里做了一件事,你想避免/解决不同的问题 -return关键字的使用,你可能想对它的搜索方式施加更严格的规则。

于 2013-01-03T10:14:17.187 回答
4
(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)))))

编辑:一点解释:进行正则表达式替换比管理循环进行删除要容易得多,这样就可以摆脱状态。而且这些步骤都是互斥的,所以你可以只使用or每个选项。

rx宏是一个小 DSL,可以编译成有效的正则表达式,它也适合 lispy 语法转换,因此我可以使用当前模式的注释字符动态构建正则表达式。

于 2013-01-03T10:27:35.973 回答
3

条件

Cond 评估列表中的一系列条件,列表中的每一项都可以是一个条件,然后是可执行指令。

Emacs Lisp 手册中的示例足以说明它是如何工作的,我在这里对其进行了注释以帮助您理解它是如何工作的。

  (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

条件的每个部分都可以按照您喜欢的任何方式进行评估,上述事实x在每个条件中都是巧合的。

例如:

 (cond ((eq 1 2) "Omg equality borked!") ;; Will never be true
       (t "default")) ;; always true

所以与 switch 的比较有点有限,它本质上是一个 if 语句的列表,它执行/返回第一个 true 条件的主体列表。

希望这可以帮助您更好地理解cond

 (cond (condition body ... ) ;; execute body of 1st passing  
       (condition body ... ) ;; condition and return result  
       (condition body ... ) ;; of the final evaluation.
       ;; etc
 )  

或者

您可以执行类似于使用 OR 切换的操作,具体取决于您构建代码的方式。

这不是函数式风格,因为它依赖于副作用来做你想做的事,然后返回一个布尔值用于流控制,这里是一个伪 lisp 的例子。

(或者)

(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.
     )
)         

这是使用类似开关的结构的工作示例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))
)

插入:

hello
hello

(和)

and 运算符(不仅在 Lisp 中)也非常有用,它不会评估所有内容直到为真,它会评估为真的条件,直到评估为假。

or & and 都可以用来构建有用的逻辑树。

于 2013-01-03T09:18:12.257 回答
1

根据 Chris 的想法,我现在就是这样做的,将其分解为单独的函数会更容易。

编辑:现在还应用or了从 Slomojo 获得的这个线程中获得的知识(没有更多的变量!)

(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))
)
于 2013-01-03T10:48:59.897 回答