15.2 recursive-graph-body-print 函数

graph-body-print 函数同样可以用递归方式实现。递归方案分为两部分:外层包装函数使用 let 表达式计算只需获取一次的变量值(如图表最大高度),内层函数则通过递归调用完成图表打印。

包装函数并不复杂:

(defun recursive-graph-body-print (numbers-list)
  "根据 NUMBERS-LIST 打印柱状图。
numbers-list 由纵轴数值构成。"
  (let ((height (apply 'max numbers-list))
        (symbol-width (length graph-blank))
        from-position)
    (recursive-graph-body-print-internal
     numbers-list
     height
     symbol-width)))

递归函数则稍复杂一些。它包含四部分:继续执行判断、打印代码、递归调用和步进表达式。继续执行判断使用 when 表达式,用于确定 numbers-list 是否还有剩余元素;若有,则函数通过打印代码绘制一列图表并再次调用自身。函数会根据步进表达式生成的值递归调用,作用于长度更短的 numbers-list

(defun recursive-graph-body-print-internal
  (numbers-list height symbol-width)
  "打印柱状图。
在 recursive-graph-body-print 内部使用。"  

  (when numbers-list
        (setq from-position (point))
        (insert-rectangle
         (column-of-graph height (car numbers-list)))
        (goto-char from-position)
        (forward-char symbol-width)
        (sit-for 0)     ; Draw graph column by column.
        (recursive-graph-body-print-internal
         (cdr numbers-list) height symbol-width)))

安装后即可测试该表达式,示例如下:

(recursive-graph-body-print '(3 2 5 6 7 5 3 4 6 4 3 2 1))

recursive-graph-body-print 生成的图表效果如下:

                *
               **   *
              ****  *
              **** ***
            * *********
            ************
            *************

graph-body-printrecursive-graph-body-print 这两个函数均可生成图表主体。