【问题标题】:Emacs Lisp: "if: No catch for tag: --cl-block-nil--, t" when returning nilEmacs Lisp:“如果:没有捕获标记:--cl-block-nil--,t”返回零时
【发布时间】:2013-11-08 15:55:27
【问题描述】:

我今天尝试编写一个 elisp 函数,一切正常,除了当函数返回 nil 时不断抛出错误。这是函数:(请原谅格式,我还不确定如何缩进。)

(defun byte-is-readable (byte)
  "Determine whether BYTE is readable or not.
Returns t or nil."
  (if (and (<= byte 126) (>= byte 32)) t nil))

;; Read the 100 bytes of a file
;; If 10 in a row are not 'readable', it is safe to assume the file is a binary
(defun is-file-binary (file)
  "Determine whether FILE is a binary file.
Returns t or nil."
  (let* ((i 1)
         (c 0)
         (size (nth 7 (file-attributes file)))
         (lim (if (< size 100) size 100)))
    (while (< i lim)
      (let ((char (with-temp-buffer
                    (insert-file-contents file)
                    (buffer-substring i (+ i 1)))))
        (if (not (byte-is-readable (aref char 0)))
            (setq c (+ c 1))
          (setq c 0))
        (if (= c 10) (return t)))
      (setq i (+ i 1))))
  nil)

这样调用它:(message "%s" (if (is-file-binary "/path/to/some/file") "true" "false") 在返回 true 时工作文件,但在返回 nil 时抛出“if: No catch for tag: --cl-block-nil--, t”。我猜这是因为 nil 没有被正确评估或其他原因。

我该如何解决?

【问题讨论】:

  • 您似乎正在创建一个新的临时缓冲区,并在每次通过 WHILE 循环时将 FILE 的内容加载到其中。这似乎效率低下。为什么不将 WITH-TEMP-BUFFER 包裹在 WHILE 周围? (with-temp-buffer (insert-file-contents file) (while (&lt; i lim) (let ((char (buffer-substring i (1+ i)))) ... )) 这样,您只需加载 FILE 的内容一次,而不是最多 100 次——对于小文件,它不会有什么不同,但是,因为您将整个文件加载到缓冲区中,对于大文件,它很可能。 (额外的功劳:只加载你需要的字节。)
  • 另外,你根本不需要打破 WHILE 循环;您可以简单地让它自己耗尽,之后您将获得 C 中“可读”字节的计数。然后,用(&lt; c 10) 替换 LET 表单末尾的裸露 NIL,如果存在,该函数将返回 T少于十个“可读”字节,如果有十个或更多,则为 NIL。
  • 你也可以去掉 LIM 绑定,因为你只在一个地方使用它;只需从 LET 绑定表单中删除该绑定,然后将 (if (&lt; size 100) size 100) 表单用于 WHILE 测试中
  • @AaronMiller:考虑改写您的 cmets 作为答案。例如,它们提供的答案与 wvxvw 的答案一样多。
  • 你不需要这一切:(if (and (&lt;= byte 126) (&gt;= byte 32)) t nil)),除非你真的关心t 作为非nil 值。这足够了:(and (&lt;= byte 126) (&gt;= byte 32)).

标签: function emacs elisp


【解决方案1】:

错误来自您在代码中使用了return。这是一个宏,仅在其他一些 cl 宏的上下文中具有有意义的扩展,例如 loop

我鼓励您使用loop 而不是while,因为它允许隐藏无趣的实现细节,例如计数器、throw - catch 机制等。但是如果您出于任何原因想要使用@ 987654328@ 打破while 循环的方法是使用:

(catch 'label (while <condition> ... (throw 'label <result>)))

使用loop 的代码版本。

(defun byte-readable-p (byte)
  "Determine whether BYTE is readable or not.
Returns t or nil."
  (cl-case byte
    ((?\n ?\r ?\t) t)
    (otherwise (and (<= byte 126) (>= byte 32)))))

(defun file-binary-p (file)
  "Determine whether FILE is a binary file.
Returns t or nil."
  (with-temp-buffer
    (insert-file-contents file)
    (cl-loop for c across
             (buffer-substring-no-properties
              1 (min 100 (buffer-size)))
             thereis (not (byte-readable-p c)))))

(file-binary-p (expand-file-name "~/.emacs"))
nil

(file-binary-p (expand-file-name "~/.emacs.d/elpa/w3m-20131021.2047/w3m.elc"))
t

或者,甚至更短的版本,使用高阶函数:

(defun file-binary-p (file)
  "Determine whether FILE is a binary file.
Returns t or nil."
  (with-temp-buffer
    (insert-file-contents file)
    (cl-find-if-not 'byte-readable-p
                    (buffer-substring-no-properties
                     1 (min 100 (buffer-size))))))

请注意,在 Lisp 代码中,返回布尔值(通常称为“谓词”)的函数通常使用以下方案命名:&lt;word&gt;p&lt;word&gt;-&lt;word&gt;-p,而不是 is-&lt;word&gt;

【讨论】:

  • 像魅力一样工作,谢谢。我对这些功能的作用有点困惑。他们是否检查所有 100 个字节都是“可读的”?因为那样的话,对于确实具有“可读”字符的文件(elf 文件以字母 ELF 开头,而 zip 以 PK 开头),这将失败,但事实并非如此。
  • @ronmrdechai 函数在文件开头的一百个或更少字节中搜索超出byte-readable-p 设置的范围的第一个字节(实际上是字符)。
猜你喜欢
  • 1970-01-01
  • 2013-07-24
  • 1970-01-01
  • 2012-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多