0

Just started learning lisp and I need to write a function, COUNTER, that would do as mentioned in the title. For example,

(COUNTER 'fizz '(fizz (buzz (fizz) (buzz fizz)) fizz))

should return 4

First try

I've written the following

(defun COUNTER (atom list)
  (cond
   ((null list) 0)
   ((equal atom (first list)) (+ 1 (COUNTER atom (rest list))))
   (t (COUNTER atom (rest list)))))

Which only counts the atom at the top most level (in the case of the example, my output is 2). What do I need to add to have it read the nested lists? I know I should be traversing the sublist and sum the results, but I'm not exactly sure which predicate I should be using and where exactly I should place it. I'm thinking right after

((equal atom (first list)) (+ 1 (COUNTER atom (rest list))))

Second try

I have tried the following to no avail

(defun COUNTER (target nest_list)
  (cond ((null nest_list) 0)
    (cond (atom (first nest_list) ((equal target (first nest_list)) (+ 1 (COUNTER target (rest nest_list)))))
      (t (COUNTER target (first nest_list))))
    (t (COUNTER target (rest nest_list)))))

I am receiving compilation errors from it. The logic to me seems to make sense. I'm testing to see whether the entity is an atom or a list, if atom, then see if it's equal and add 1, otherwise recur if it's a list. I'm not sure if I'm doing something wrong with the parenthesis placement or whether I'm allowed to use a cond within a cond like that.

coredump
  • 37,664
  • 5
  • 43
  • 77
trungnt
  • 1,111
  • 1
  • 7
  • 19

2 Answers2

1

You can close over a counter to avoid passing too many parameters. Also, you probably should pass a custom test function like CL functions do.

(defun counter (search tree &key (test #'eql))
  (let ((total 0))
    (labels
        ;; Our tree walker. Both counter and search are accessible
        ;; from the lexical environment, so there is no need to
        ;; pass them as arguments.
        ((walk (tree)
           (cond
             ;; First, see if current element matches the search.
             ;; That allows to search for a list inside a tree.
             ((funcall test tree search) (incf total))

             ;; Otherwise, if this is a cons-cell, recurse in CAR and CDR
             ((consp tree)
              (walk (car tree))
              (walk (cdr tree))))))

      ;; Initiate walk
      (walk tree)

      ;; Return total
      total)))

If this is a homework, I don't think this could satisfy your requirements (you are probably expected to provide a purely recursive function). However, if you understand the above, it should not be hard to build a recursive approach that never mutates variables.

For reference:

Example

(counter '(3 2)
         '(a b (c d 3) (3 2) e f)
         :test #'equalp)
=> 1

(counter 3 '(a b (c d 3) (3 2) e f))
=> 2
coredump
  • 37,664
  • 5
  • 43
  • 77
  • Thanks for the suggestion. I am doing this for an assignment and need to work strictly with recursion. I have tried the following (shown in my answer above) and am getting compilation errors – trungnt Oct 03 '16 at 18:05
0

You also need to check whether first list is, itself, an atom or a list. If it's an atom, then your current logic is correct; if it's a list, then you also need to recur on this list.

I removed my comment about parameter names, since it's not a problem in most full-featured LISP versions. Thanks to coredump for the correction.

(defun COUNTER (target nest_list)
  (cond ((null nest_list) 0)
    (cond (atom (first nest_list))
      (equal target (first nest_list)) (+ 1 (COUNTER target (rest nest_list))))
      (t (COUNTER target (rest nest_list)))))

Now, in the else branch of the new cond, insert another recursion:

(COUNTER (target (first nest_list)))

You'll need to add this (instead of 1) to the count for (rest nest_list). Does this get you moving? I know I haven't been careful with my structure, but I'm trying to avoid doing the job for you.

Prune
  • 76,765
  • 14
  • 60
  • 81
  • This definitely helps. Thank you. I didn't think to use cond several times. But it makes sense since I'm trying to look into nested lists. Just a quick questions. When you mention (COUNTER (target atom)), do you mean (target nest_list)? – trungnt Oct 03 '16 at 17:04
  • 1
    Actually, I meant the head of the list -- which we now know is a list, itself. When you get this solved, please remember to come back and properly retire the question: accept an answer (even if you have to write it yourself), delete the question (unlikely; I think this deserves to be kept), or other method of closing it for the archives. – Prune Oct 03 '16 at 17:10
  • Also, is the else branch within the second cond or would I have to create a new cond? Sorry, I'm really new to LISP so I'm still trying to understand the structure and syntax. – trungnt Oct 03 '16 at 17:10
  • That **else** is from the second **cond**. – Prune Oct 03 '16 at 17:11
  • 1
    If you're new to LISP, would it help to use the language facilities that are a little closer to procedural languages? You can set local variables, and there's even an **if** that you might find more readable. For one thing, local variables would avoid a lot of repetition of **first nest_list**, **rest nest_list** and duplicated calls. If you like, when you get that all working -- with your eyeballs intact -- *then* you could make the text replacements to eliminate the temporary variables. – Prune Oct 03 '16 at 17:14
  • 2
    *"In this case, it keeps you from using the ATOM predicate."*. That would be true in Scheme. This isn't the case in Common Lisp, which is the language used here. Also, if your code manipulates lists, it makes sense to use variables named `list`. – coredump Oct 03 '16 at 17:19
  • 1
    I tried the following listed in my own answer and it's not compiling correctly. I'm not sure whether it's because I'm structuring it correctly or something else. Am I actually able to use a cond within a cond? The error I'm receiving is "undefined variable: cond" – trungnt Oct 03 '16 at 18:08
  • That would be some sort of syntax problem. Yes, you can nest **cond** invocations. – Prune Oct 03 '16 at 18:23
  • 1
    @coredump; thanks for the update. I've used LISP off an on (mostly off) over several decades, and some of my habits are formed from the early deficiencies. – Prune Oct 03 '16 at 18:24