如何在递归循环中正确统计计数?Lisp递归函数计数异常排查
问题描述
我编写了一个用于统计文件中内容出现次数的递归函数,习惯使用format函数输出函数执行结果。以下是我的Lisp代码:
(defun csv-counter (list) (let ((counter 0) (email (first list))) (if (null list) nil (progn (+ 1 (count email list :test #'string=)) (incf counter) (csv-counter (rest list)))) (format t "count for email ~a is ~a~%" email counter)))
但format函数中的计数器并未累加统计总数,而是每次输出均为1,输出示例如下:
... count for email fred@test.com is 1 count for email fred@test.com is 1 count for email fred@test.com is 1 ...
请问我哪里出错了?
问题分析与解决方案
你的代码存在三个核心问题:
- 计数器无法累加:每次递归调用
csv-counter时,let都会创建一个全新的counter变量并初始化为0,之后仅执行一次incf counter将其变为1。每个递归层的counter都是独立的局部变量,根本无法实现跨层累加。 - 无效计数代码:
(+ 1 (count email list :test #'string=))这个表达式完全无用——count已经能直接返回email在list中的出现次数,你既没把这个结果赋值给counter,也没做任何其他处理。 - 重复统计输出:递归遍历每个元素时,会对重复邮箱多次统计并输出,导致同一邮箱的结果被打印多次,而非只输出一次总计数。
修正后的实现
方案1:哈希表统计(高效简洁)
用哈希表一次性统计所有邮箱的出现次数,再统一输出,这是最常用的实现方式:
(defun csv-counter (list) (let ((count-table (make-hash-table :test #'equal))) ;; 遍历列表,累加每个邮箱的计数 (dolist (email list) (incf (gethash email count-table 0))) ;; 遍历哈希表输出结果 (maphash (lambda (email count) (format t "count for email ~a is ~a~%" email count)) count-table)))
方案2:递归传递统计状态
如果坚持用递归实现,可以通过可选参数传递哈希表,在递归过程中累积计数:
(defun csv-counter-recursive (list &optional (count-table (make-hash-table :test #'equal))) (if (null list) ;; 列表遍历完成后输出所有统计结果 (maphash (lambda (email count) (format t "count for email ~a is ~a~%" email count)) count-table) ;; 累加当前邮箱的计数,继续递归 (progn (incf (gethash (first list) count-table 0)) (csv-counter-recursive (rest list) count-table))))
内容的提问来源于stack exchange,提问作者Vinn
相关产品推荐
相关产品推荐

