5

I tried my hand at writing an elisp function today, all works well except for an error that keeps getting thrown when the function returns nil. Here's the function: (Please excuse the formatting, I'm not really sure how to indent this yet.)

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

Calling it like this: (message "%s" (if (is-file-binary "/path/to/some/file") "true" "false") works file when returning true, but throws "if: No catch for tag: --cl-block-nil--, t" when returning nil. I'm guessing this is because nil isn't evaluated correctly or something.

How do I fix it?

4

1 回答 1

5

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

我鼓励你使用loopthen while,因为它允许隐藏无趣的实现细节,例如计数器、throw-catch机制等。但是如果你出于任何原因想要使用whilewhile循环中中断的方法是使用:

(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 代码中,返回布尔值(通常称为“谓词”)的函数使用方案命名:<word>p或者<word>-<word>-p更确切地说是 then is-<word>

于 2013-11-08T16:11:04.440 回答