C.2.1 附录:计算余数

在Lisp中,用于计算余数的函数是%。该函数返回其第一个参数除以第二个参数的余数。正巧,%在Emacs Lisp中是一个无法通过apropos发现的函数:如果输入M-x apropos RET remainder RET,你将找不到任何结果。了解%存在的唯一方式是通过阅读本书或Emacs Lisp源代码。

你可以通过评估以下两个表达式来尝试%函数:

(% 7 5)

(% 10 5)

第一个表达式返回2,第二个表达式返回0。

为了测试返回值是否为零或其他数字,我们可以使用zerop函数。该函数在其参数(必须是数字)为零时返回t

(zerop (% 7 5))
     ⇒ nil

(zerop (% 10 5))
     ⇒ t

因此,以下表达式将在图的高度能够被五整除时返回t

(zerop (% height 5))

(当然,height的值可以通过(apply 'max numbers-list)获得。)

另一方面,如果height的值不是五的倍数,我们希望将其重置为下一个更高的五的倍数。这是使用我们已经熟悉的函数进行的简单算术。首先,我们将height的值除以五,以确定五可以整除该数多少次。因此,五可以整除十二两次。如果我们将这个商加一并乘以五,就会得到大于该高度的下一个五的倍数的值。五可以整除十二两次。将二加一,并乘以五;结果是十五,这是大于十二的下一个五的倍数。这个Lisp表达式是:

(* (1+ (/ height 5)) 5)

例如,如果你评估以下表达式,结果将是15:

(* (1+ (/ 12 5)) 5)

在整个讨论中,我们一直使用5作为Y轴标签间隔的值;但我们可能想要使用其他值。为了通用性,我们应该用一个变量替换5,以便我们可以为其赋值。我能想到的这个变量的最佳名称是Y-axis-label-spacing

使用这个术语和一个if表达式,我们可以得到以下结果:

(if (zerop (% height Y-axis-label-spacing))
    height
  ;; 否则
  (* (1+ (/ height Y-axis-label-spacing))
     Y-axis-label-spacing))

如果高度是Y-axis-label-spacing的偶数倍,此表达式将返回height本身,否则它计算并返回一个等于Y-axis-label-spacing的下一个更高倍数的height值。

现在,我们可以将此表达式包含在print-graph函数的let表达式中(首先设置Y-axis-label-spacing的值):

(defvar Y-axis-label-spacing 5
  "从一个Y轴标签到下一个的行数。")

…
(let* ((height (apply 'max numbers-list))
       (height-of-top-line
        (if (zerop (% height Y-axis-label-spacing))
            height
          ;; 否则
          (* (1+ (/ height Y-axis-label-spacing))
             Y-axis-label-spacing)))
       (symbol-width (length graph-blank))))
…

(注意使用let*函数:通过(apply 'max numbers-list)表达式计算一次height的初始值,然后使用height的结果值计算其最终值。有关let*的更多信息,请参阅See The let* expression.)