【问题标题】:Lisp: cycle through functionsLisp:循环函数
【发布时间】:2015-12-29 05:50:22
【问题描述】:

我使用 elfeed 读取 emacs 中的 RSS,并希望制作一个循环浏览不同视图的函数:新闻、9gag、非新闻等。

我试图通过快速研究来编写一些代码。当我按 ctrl + 右箭头键时,它会在视图中循环。它工作正常,但我认为它不是最方便的代码。

我还想让它在按 ctrl + 左箭头键时反向浏览视图。

;;cycle through views
(setq a 0)
(defun cycle-list-view ()
  (interactive)
  (if (= a 3)
    (progn
    (elfeed-read-9gag)
    (setq a 4)
    ))
  (if (= a 2)
    (progn
    (elfeed-read-nonnews)
    (setq a 3)      
    ))
  (if (= a 1)
    (progn
    (elfeed-read-news)
    (setq a 2)      
    ))
  (if (= a 0)
    (progn
    (elfeed-read-defaultnews)
    (setq a 1)      
    ))
  (if (= a 4)
    (progn
    (setq a 0)
    ))
  )

 (global-set-key (kbd "<C-right>") 'cycle-list-view)

【问题讨论】:

  • 见函数set-transient-map。您可以使用它来循环而不需要全局变量。或者测试last-command,如果是相同的命令则循环。 (实际上有几种方法可以在各种行为之间循环。)

标签: emacs elisp elfeed


【解决方案1】:

看起来 Tripleee 的答案适用于您提出的一般问题(关于如何循环浏览函数列表)。对于特定的问题(通过 Elfeed 过滤器循环),我不久前为自己编写了一个实现,并认为我会在此处发布它以防它对您有用。

没有看到您正在调用的代码(例如,elfeed-read-news),很难说这与您的实现有何不同,但我猜您不需要将所有视图写成单独的函数(除非您还希望命令直接跳转到特定视图)。

另外,我应该注意我的实现使用了 dash 库,您可以在 MELPA 上找到它。

(defvar my/elfeed-favorite-filters
  '("@6-months-ago +unread"
    "+todo")
  "A list of commonly-used filters for Elfeed.
Use `my/elfeed-search-next-favorite-filter' to cycle through
these.")

(defun my/elfeed-search-next-favorite-filter (&optional verbose)
  "Apply the next filter in `my/elfeed-favorite-filters'.

If the current search filter is an element of
`my/elfeed-favorite-filters', apply the filter immediately
following that one in the list, looping back to the beginning if
necessary.

If the current search filter is not an element of
`my/elfeed-favorite-filters', apply the first filter in the
list.

If `my/elfeed-favorite-filters' is empty, just apply the default
filter.

Return the filter applied.  When called interactively or the optional
VERBOSE parameter is non-nil, also print a message informing the user
of the newly applied filter."
  (interactive "p")
  (let ((new-filter
         (my/successor-in-list my/elfeed-favorite-filters
                                elfeed-search-filter :cyclical)))
    (elfeed-search-set-filter new-filter)
    (when verbose (message "Filter applied: %s" elfeed-search-filter))
    elfeed-search-filter))

(defun my/successor-in-list (list elt &optional cycle)
  "Return the element in LIST following ELT.
If ELT is not an element of LIST, return nil.

If ELT is the last element in LIST, return (car LIST) if the
optional parameter CYCLE is non-nil; otherwise, return nil."
  (require 'dash)                       ; For `-drop-while'
  (let ((found  (-drop-while (lambda (x) (not (equal x elt)))
                             list)))
    (cond
     ((null found)                   nil)
     ((or (cdr found) (null cycle))  (cadr found))
     (:else                          (car list)))))

我不使用“循环向后”命令,但很容易添加:

(defun my/elfeed-search-prev-favorite-filter (&optional verbose)
  "Apply the previous filter in `my/elfeed-favorite-filters'.

As `my/elfeed-search-next-favorite-filter', but cycle backwards."
  (interactive "p")
  (let ((my/elfeed-favorite-filters (reverse my/elfeed-favorite-filters)))
    (my/elfeed-search-next-favorite-filter verbose)))

【讨论】:

    【解决方案2】:

    您可能应该将数据放在一个单独的变量中,并让您的代码在该结构中简单地维护一个全局索引变量。可能是这样的:

    (defvar elfeed-views-list "List of views for `elfeed-cycle-views'.
    Each member should be a cons of a label string and a function.
    `elfeed-views--index' is a pointer into this structure."
      (("default" . #'elfeed-read-defaultnews)
       ("news" . #'elfeed-read-news)
       ("nonnews" . #'elfeed-read-nonnews)
       ("9gag" . #'elfeed-read-9gag)))
    

    添加新视图不再需要更改代码 - 只需将另一个项目推到此列表的末尾即可。

    现在循环函数需要做的就是增加或减少这个结构的索引,如果你超过了结尾,请注意包装:

    ;; The double dash in the name suggests a private variable
    (defvar elfeed-views--index "Index into `elfeed-views-list' for `elfeed-cycle-views'.
    Use that function to manipulate the index." 0)
    
    (defun elfeed-cycle-views (&optional skip)
      "Update `elfeed-views--index' by adding SKIP (default 1) and
    wrapping if necessary, then call the function at this index in
    `elfeed-views-list'."
      (interactive)
      (setq elfeed-views--index (+ elfeed-views-index (or skip 1)))
       ;; BUG: skips with an absolute value bigger than 1 don't wrap properly.
      (if (>= elfeed-views--index (length elfeed-views-list))
        (setq elfeed-views--index 0)
       (when (< elfeed-views--index 0)
         (setq elfeed-views--index (1- (length elfeed-views-list))) ))
      (eval (cdr (nth elfeed-views--index elfeed-views-list))) )
    

    现在可以通过传递 -1 作为 SKIP 参数来简单地实现其他方式的循环。

    (未经测试,但至少这应该给你一些想法。)

    除了文体问题,您想了解cond 而不是可怕的if 语句序列。您显然不得不强制您的代码采用不直观的反向排序顺序,因为您不了解此控制结构。

    【讨论】:

      猜你喜欢
      • 2018-09-23
      • 2014-09-10
      • 2012-04-27
      • 1970-01-01
      • 2020-09-10
      • 1970-01-01
      • 2013-03-10
      • 2013-02-12
      • 1970-01-01
      相关资源
      最近更新 更多