简体   繁体   English

我如何在此递归循环中捕获计数?

[英]How do I catch the count in this recursive loop?

I have a recursive function that counts the number of occourances in a file.我有一个递归的 function 来计算文件中出现的次数。

A common task I like to do is report the outcome of a function with a format :我喜欢做的一项常见任务是报告 function 的结果, format如下:


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


The counter number in the format function doesnt actually accumulate the total number, instead it reports each occurance as 1格式为 function 的计数器编号实际上并不累加总数,而是将每次出现报告为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
... 

What am I doing wrong?我究竟做错了什么?

It is unclear what your function is meant to do or what you are trying to achieve.目前尚不清楚您的 function 是做什么的或您正在尝试实现什么。 Even so it's possible to say some things about it.即便如此,也可以说一些关于它的事情。 Below I have reindented it and annotated some points with numbers下面我重新缩进了它并用数字注释了一些点

(defun csv-counter (list)
  (let ((counter 0)
        (email (first list)))
    ;; (0)
    (if (null list)
        nil
      (progn
        (+ 1 (count email list :test #'string=)) ;(1)
        (incf counter)                           ;(2)
        (csv-counter (rest list))))
    ;; (3)
    (format t "count for email ~a is ~a~%" email counter)))

At (0) counter will be zero, on every call.(0)处,每次调用时counter都将为零。

At (1) is an expression, (+ 1 (count email list:test #'string=)) whose value is not used.(1)处是一个表达式, (+ 1 (count email list:test #'string=))其值未被使用。 So this expression does not do anything useful at all: it merely serves to make the time complexity quadratic rather than linear.所以这个表达式根本没有做任何有用的事情:它只是使时间复杂度变为二次而不是线性。

At (2) counter is incremented by 1, which means it will now be 1. The result of (2) is that, if the list is not empty, the value of counter will be 1.(2)处, counter加 1,这意味着它现在将为 1。(2) 的结果是,如果列表不为空,则counter的值将为 1。

At (3) this value is reported: it will be 1 if the list is not empty, 0 otherwise.(3)处报告此值:如果列表不为空,则为 1,否则为 0。

So we get:所以我们得到:

count for email nil is 0
count for email fish@bat is 1
count for email foo@bar is 1
count for email foo@bar is 1

Now, as I said above, it is not clear what you are trying to achieve.现在,正如我上面所说,目前尚不清楚您要实现的目标。 However, it might be to count the number of distinct occurrences of each email address (represented as a string) in a list of them.但是,它可能是计算地址列表中每个 email 地址(表示为字符串)的不同出现次数。 So for instance, given ("foo@bar" "foo@bar" "fish@bat") you want a count of 2 for "foo@bar and of 1 for "fish@bat" .因此,例如,给定("foo@bar" "foo@bar" "fish@bat")您希望 " "foo@bar @bar" 的计数为 2," "fish@bat" @bat" 的计数为 1。

In order to do this you need two things: a count for each email, and a notion of which emails you have seen .为此,您需要做两件事:每个 email 的计数,以及您看到了哪些电子邮件的概念 The second is crucial.第二个很关键。

So here is an initial approach to doing this:因此,这是执行此操作的初始方法:

(defun count-distinct-emails (emails)
  (labels ((cde-loop (tail seen counts)
             (cond
              ((null tail)
               counts)
              ((member (first tail) seen :test #'string=)
               ;; already counted this one
               (cde-loop (rest tail) seen counts))
              (t
               ;; a new email
               (let ((email (first tail))
                     (more (rest tail)))
                 (cde-loop more
                           (cons email seen)
                           (acons email (+ 1 (count email more :test #'string=)) counts)))))))
    (cde-loop emails '() '())))

This function is not itself recursive, but it has a recursive helper function, cde-loop , which is written as an internal definition.这个 function 本身不是递归的,但是它有一个递归的 helper function, cde-loop ,它被写成一个内部定义。 It is written as an internal function to avoid the nightmare of needing all sorts of weird extra, perhaps optional, arguments to the function you actually call and because it is not called by any other function than its parent.它被编写为内部 function 以避免需要各种奇怪的额外的噩梦,也许是可选的,arguments 到您实际调用的 function,因为除了它的父级之外,它不会被任何其他 function 调用。 In cde-loop you can see that it maintains a table (a list) of emails it has seen, and builds up another table (an alist) of addresses with counts.cde-loop中,您可以看到它维护了一个已查看电子邮件的表(列表),并构建了另一个包含计数的地址表(列表)。

For this function:对于这个 function:

> (count-distinct-emails '("foo@bar" "foo@bar" "fish@bat"))
(("fish@bat" . 1) ("foo@bar" . 2))

And you can then write a little reporter function:然后你可以写一个小记者function:

(defun report-emails (table)
  (dolist (email&count table)
    (format t "~&count for ~A: ~D~%"
            (car email&count) (cdr email&count))))

So:所以:

> > (report-emails (count-distinct-emails '("foo@bar" "foo@bar" "fish@bat")))
count for fish@bat: 1
count for foo@bar: 2
nil

Now count-distinct-emails is horrible: not because it's recursive (any reasonable implementation will turn that into a loop) but because it's repeatedly probing the list of things it has seen and the list of emails it is looking for.现在count-distinct-emails很可怕:不是因为它是递归的(任何合理的实现都会把它变成一个循环)而是因为它反复探测它所看到的事物列表和它正在寻找的电子邮件列表。 A much better approach is to unify these two things into one thing, and use a hashtable which has better search performance:一个更好的方法是将这两个东西统一为一个东西,并使用具有更好搜索性能的哈希表:

(defun count-distinct-emails (emails)
  (labels ((cde-loop (tail table)
             (if (null tail)
                 table
               (progn
                 (incf (gethash (first tail) table 0))
                 (cde-loop (rest tail) table)))))
    (cde-loop emails (make-hash-table :test #'equal))))

And then the reporter function needs to know to use a hashtable as well:然后记者 function 也需要知道使用哈希表:

(defun report-emails (table)
  (maphash (lambda (email count)
             (format t "~&count for ~A: ~D~%"
                     email count))
           table))

Note that cde-loop uses a nice trick: it says (incf (gethash (first tail) table 0)) : incf knows how to increment the value of an entry in a hashtable, and using the default of 0 when the entry is not present means that the entry will spring into being so you don't have to do the awkward 'check if entry is present, increment if so' thing yourself.请注意, cde-loop使用了一个很好的技巧:它说(incf (gethash (first tail) table 0))incf知道如何增加哈希表中条目的值,并在条目不存在时使用默认值0 present 意味着该条目将 spring 变为存在,因此您不必自己做尴尬的“检查条目是否存在,如果存在则递增”。

Finally, once you've given in and used a hashtable, this is a case where a straightforward iterative solution is probably clearer:最后,一旦您输入并使用了哈希表,在这种情况下,直接的迭代解决方案可能会更清晰:

(defun count-distinct-emails (emails)
  (let ((table (make-hash-table :test #'equal)))
    (dolist (email emails table)
      (incf (gethash email table 0)))))

For completeness I think you could use remove-duplicates here:为了完整起见,我认为您可以在此处使用remove-duplicates

(defun count-distinct-emails (emails)
  (length (remove-duplicates emails :test #'string=)))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM