How do I catch the count in this recursive loop? - recursion

I have a recursive function that counts the number of occourances in a file.
A common task I like to do is report the outcome of a function with a 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
...
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. 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.
At (1) is an expression, (+ 1 (count email list :test #'string=)) whose value is not used. 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.
At (3) this value is reported: it will be 1 if the list is not empty, 0 otherwise.
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. 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".
In order to do this you need two things: a count for each email, and a notion of which emails you have seen. 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. 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. 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.
For this 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:
(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. 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:
(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.
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:
(defun count-distinct-emails (emails)
(length (remove-duplicates emails :test #'string=)))

Related

What exactly does the #. (sharpsign dot) do in Common Lisp? Is it causing a variable has no value error?

Edit: Title updated to reflect what my question should have been, and hopefully lead other users here when they have the same problem.
Little bit of a mess, but this is a work-in-progress common lisp implementation of anydice that should output some ascii art representing a probability density function for a hash-table representing dice rolls. I've been trying to figure out exactly why, but I keep getting the error *** - SYSTEM::READ-EVAL-READER: variable BAR-CHARS has no value when attempting to run the file in clisp. The error is originating from the output function.
The code is messy and convoluted (but was previously working if the inner most loop of output is replaced with something simpler), but this specific error does not make sense to me. Am I not allowed to access the outer let* variables/bindings/whatever from the inner most loop/cond? Even when I substitute bar-chars for the list form directly, I get another error that char-decimal has no value either. I'm sure there's something about the loop macro interacting with the cond macro I'm missing, or the difference between setf, let*, multiple-value-bind, etc. But I've been trying to debug this specific problem for hours with no luck.
(defun sides-to-sequence (sides)
(check-type sides integer)
(loop for n from 1 below (1+ sides) by 1 collect n))
(defun sequence-to-distribution (sequence)
(check-type sequence list)
(setf distribution (make-hash-table))
(loop for x in sequence
do (setf (gethash x distribution) (1+ (gethash x distribution 0))))
distribution)
(defun distribution-to-sequence (distribution)
(check-type distribution hash-table)
(loop for key being each hash-key of distribution
using (hash-value value) nconc (loop repeat value collect key)))
(defun combinations (&rest lists)
(if (endp lists)
(list nil)
(mapcan (lambda (inner-val)
(mapcar (lambda (outer-val)
(cons outer-val
inner-val))
(car lists)))
(apply #'combinations (cdr lists)))))
(defun mapcar* (func lists) (mapcar (lambda (args) (apply func args)) lists))
(defun dice (left right)
(setf diceprobhash (make-hash-table))
(cond ((integerp right)
(setf right-distribution
(sequence-to-distribution (sides-to-sequence right))))
((listp right)
(setf right-distribution (sequence-to-distribution right)))
((typep right 'hash-table) (setf right-distribution right))
(t (error (make-condition 'type-error :datum right
:expected-type
(list 'integer 'list 'hash-table)))))
(cond ((integerp left)
(sequence-to-distribution
(mapcar* #'+
(apply 'combinations
(loop repeat left collect
(distribution-to-sequence right-distribution))))))
(t (error (make-condition 'type-error :datum left
:expected-type
(list 'integer))))))
(defmacro d (arg1 &optional arg2)
`(dice ,#(if (null arg2) (list 1 arg1) (list arg1 arg2))))
(defun distribution-to-probability (distribution)
(setf probability-distribution (make-hash-table))
(setf total-outcome-count
(loop for value being the hash-values of distribution sum value))
(loop for key being each hash-key of distribution using (hash-value value)
do (setf (gethash key probability-distribution)
(float (/ (gethash key distribution) total-outcome-count))))
probability-distribution)
(defun output (distribution)
(check-type distribution hash-table)
(format t " # %~%")
(let* ((bar-chars (list 9617 9615 9614 9613 9612 9611 9610 9609 9608))
(bar-width 100)
(bar-width-eighths (* bar-width 8))
(probability-distribution (distribution-to-probability distribution)))
(loop for key being each hash-key of
probability-distribution using (hash-value value)
do (format t "~4d ~5,2f ~{~a~}~%" key (* 100 value)
(loop for i from 0 below bar-width
do (setf (values char-column char-decimal)
(truncate (* value bar-width)))
collect
(cond ((< i char-column)
#.(code-char (car (last bar-chars))))
((> i char-column)
#.(code-char (first bar-chars)))
(t
#.(code-char (nth (truncate
(* 8 (- 1 char-decimal)))
bar-chars)))))))))
(output (d 2 (d 2 6)))
This is my first common lisp program I've hacked together, so I don't really want any criticism about formatting/style/performance/design/etc as I know it could all be better. Just curious what little detail I'm missing in the output function that is causing errors. And felt it necessary to include the whole file for debugging purposes.
loops scoping is perfectly conventional. But as jkiiski says, #. causes the following form to be evaluated at read time: bar-chars is not bound then.
Your code is sufficiently confusing that I can't work out whether there's any purpose to read-time evaluation like this. But almost certainly there is not: the uses for it are fairly rare.

Function call stack overflow

I get the error message below when I run my code. The code below add the elements that are not nil.
(summit2 '(0 nil 1 2))
Error: Received signal number -3 (function call stack overflow (delayed response))
[condition type: ASYNCHRONOUS-OPERATING-SYSTEM-SIGNAL]
I have tried changing null with nil. I also tried using eq as opposed to eql.
(defun summit2 (lst)
(if (eql (car lst) null)
(summit2 (cdr lst))
(+ (car lst) (summit2 (cdr lst)))))
the expected output should be 3, the sum of the elements in the list that are not nil
First of all, the check for nil should be done by using the NULL function, so (null (car lst)) in your case. Secondly, your recursion lacks the base case (the error you're getting indicates a stack overflow due to infinite recursion). Now you only distinguish between whether the next element is nil or non-nil. You need a third case to handle the empty list. This suggests the use of COND. You could, for example do something like:
(defun summit2 (lst)
(cond
((null lst)
0)
((null (car lst))
(summit2 (cdr lst)))
(t
(+ (car lst) (summit2 (cdr lst))))))
Some remarks
You can spell your list list, there is no need to abbreviate it to lst.
All branches in your code leads to a recursive call to summit, there is no case where you stop computing the result. This infinite computation takes up stack space, which is why it eventually stops with a stack overflow.
Please indent your code in a conventional way (read for example this style guide)
Notice that the case where an element is nil is not much different than the case where the input list is empty. With the following code, both are handled the same way:
(defun sum (val)
(if (consp val)
(+ (sum (car val))
(sum (cdr val)))
(or val 0)))
This also means the code is able to do more than expected, i.e. you can sum numbers in a tree:
(sum '(0 1 2 nil 4 5 7 (1 2)))
=> 22
And it also works when the input is just an number:
(sum 5)
=> 5

LISP Cannot take CAR of T

I am trying to evaluate each atom of a list and see if it's equal to the number provided and remove if its not but I am running into a slight problem.
I wrote the following code:
(defun equal1(V L)
(cond((= (length L) 0))
(T (cond( (not(= V (car(equal1 V (cdr L))))) (cdr L) )))
)
)
(equal1 5 '(1 2 3 4 5))
I obtain the following error
Error: Cannot take CAR of T.
If I add (write "hello") for the action if true, the following error is obtained:
Error: Cannot take CAR of "hello".
I'm still quite new to LISP and was wondering what exactly is going on and how could I fix this so I could evaluate each atom properly and remove it if its not, thus the cdr L for the action.
car and cdr are accessors of objects of type cons. Since t and "hello" are not cons you get an error message.
To fix it you need to know what types your function returns and not car unless you know that it's a cons
EDIT
First off ident and clean up the code.. The nested cond are uneccesary since cond is a if-elseif-else structure by default:
(defun remove-number (number list)
(cond ((= (length list) 0)
t)
((not (= number (car (remove-number number (cdr list)))))
(cdr list))))
(t
nil)))
I want you to notice I've added the default behaviour of returning t when a consequent is not given as we know = returns either t or nil so it returns t when the length is 0 in this case.
I've added the default case where none of the two previous predicates were truthy and it defaults to returning nil.
I've named it according to the functions used. = can only be used for numeric arguments and thus this will never work on symbols, strings, etc. You need to use equal if you were after values that look the same.
Looking at this now we can see that the functions return value is not very easy to reason about. We know that t, nil and list or any part of the tail of list are possible and thus doing car might not work or in the case of (car nil) it may not produce a number.
A better approach to doing this would be:
check if the list is empty, then return nil
check if the first element has the same numeric value as number, then recurse with rest of the list (skipping the element)
default case should make cons a list with the first element and the result fo the recursion with the rest of the list.
The code would look something like this:
(defun remove-number (number list)
(cond ((endp list) '())
((= (car list) number) (remove-number ...))
(t (cons ...))))
There are a couple of things you could do to improve this function.
Firstly, let's indent it properly
(defun equal1 (V L)
(cond
((= (length L) 0))
(T (cond
((not (= V (car (equal1 V (cdr L))))) (cdr L))))))
Rather than saying (= (length l) 0), you can use (zerop (length l)). A minor sylistic point. Worse is that branch returns no value. If the list L is empty what should we return?
The issue with the function is in the T branch of the first cond.
What we want to do is
remove any list item that is the same value as V
keep any item that is not = to V
The function should return a list.
The expression
(cond
((not (= V (car (equal1 V (cdr L))))) (cdr L)))
is trying (I think) to deal with both conditions 1 and 2. However it's clearly not working.
We have to recall that items are in a list and the result of the equal function needs to be a list. In the expression above the result of the function will be a boolean and hence the result of the function call will be boolean.
The function needs to step along each element of the list and when it sees a matching value, skip it, otherwise use the cons function to build the filtered output list.
Here is a skeleton to help you out. Notice we don't need the embedded cond and just have 3 conditions to deal with - list empty, filter a value out, or continue to build the list.
(defun equal-2 (v l)
(cond
((zerop (length L)) nil)
((= v (car l)) <something goes here>) ;skip or filter the value
(t (cons (car l) <something goes here>)))) ;build the output list
Of course, this being Common Lisp, there is a built-in function that does this. You can look into remove-if...

Common lisp hashtable

Task is to read N string like "name phone" and store in. Then find stored data with requests like "name".
My code stores names and numbers in hashtable, but after it doesn't find any values. Stored values checks with maphash (it shows all pairs key-value).
Function split-by-one-space is just utility.
(defparameter data (make-hash-table))
(defun split-by-one-space (string) ; to split string: "aaa bbb" -> (aaa bbb)
(loop for i = 0 then (1+ j)
as j = (position #\Space string :start i)
collect (subseq string i j)
while j))
(dotimes (i (read)) ; input data
(let* ((inp (read-line))
(raw (split-by-one-space inp))
(name (string (car raw)))
(phone (cadr raw)))
(format t "Adding: ~W ~W~%" name phone) ; debug
(setf (gethash name data) phone)))
(maphash #'(lambda (k v) (format t "~a => ~a~%" k v)) data) ; this show all stored data
(loop for line = (read-line *standard-input* nil :eof)
until (or (eq line :eof) (eq line nil))
do
(let ((key (gethash line data))) ; it cannot find anything. Why?
(format t "Searching: ~W~%" line) ; debug
(if (null key)
(format t "Not found~%")
(format t "~A=~A~%" (car key) (cdr key)))))
Sample input:
3
sam 99912222
tom 11122222
harry 12299933
sam
edward
harry
Unless you specify a test function, hash tables will use eql to determine "is this key identical to that key".
(defvar *s1* "a string")
(defvar *s2* "a string")
(loop for pred in '(eq eql equal equalp)
do (format t "Using ~a, the result is ~a~%"
pred (funcall pred *s1* *s2*)))
This generates the output:
Using EQ, the result is NIL
Using EQL, the result is NIL
Using EQUAL, the result is T
Using EQUALP, the result is T
In this case, the main difference between equal and equalp is that the latter is case-insensitive, while the former is not. To use another test function, use the :test keyword and one of the found "standard" test functions. If you don't need case-insensitive matches, you would simply create your hash table like this: (make-hash-table :test #'equal).

Get minimum num recursively from a list

I'm new to lisp and trying to write a recursive function that returns minimum number from a list. It also wants to detect atom. The following code returns error:
(defun minFromList (l)
(cond ((null l) nil) ; Causes error shown below
; (cond ((null l) ) ; Causes the same error
; (cond ((null l) 0) ; It causes always 0 to be the final return val.
((numberp l) l)
((numberp (car l)) (min (car l) (minFromList(cdr l))))
((listp (car l)) (min (minFromList (car l)) (minFromList (cdr l))))
(t nil) ; if all condition doesn't hold just return nil.
)
)
Error:
*** - MIN: NIL is not a real number
Apparently the problem lies in where it returns nil/0 when the given list is null. What's possible workarounds? Thank you.
Environment) Ubuntu 11.10, clisp 2.49
Update) Although I already picked up this as the answer, I welcome if there are other ways especially w/o making new functions if any.
Here's the simplest code I made inspired by the chosen answer.
(defun minNum (a b)
(cond ((null a) b)
((null b) a)
(t (min a b)))
)
Apparently you get an error message because you try to use the result of your function as a number, and said result is nil when the function is called with an empty list as argument, so the evaluation that tries to use the result fails. This is not a Common Lisp problem - you have to decide what to return when the argument is empty. Maybe 0 is a good value, maybe some approximation of minus infinity - only you (or whoever uses your function) can tell.
As for getting the the minimum (or the sum or any other 'reduction') of a list, this is a pattern already handled by the reduce Common Lisp standard function. So min-from-list could look something like:
CL-USER> (defun min-from-list (list &optional (default 0))
(reduce #'min list :initial-value default))
MIN-FROM-LIST
CL-USER> (min-from-list '(1 2 -3))
-3
CL-USER> (min-from-list '(1 2 -3) -7)
-7
CL-USER> (min-from-list '())
0
CL-USER> (min-from-list '() -3)
-3
(the user can specify what the minimum of an empty list is - if none specified, it's 0).
When comparing two numbers, you need to deal with the nil case in some way. This is easy to do. Define your own version of min that satisfies
(min2 nil <x>) = <x>
(min2 <x> nil) = <x>
(min2 <x> <y>) = (min <x> <y>) if <x>, <y> non-null
and use that.
The simplest approach I can think of is to wrap an application of min.
(defun min-or-nil (num-list)
(when num-list (apply #'min num-list)))

Resources