Как посчитать количество вхождений в списке в lisp - PullRequest
0 голосов
/ 30 сентября 2019

Назначение:

count-of (список символов) Напишите функцию с именем count-of, которая принимает два параметра, символ и список. Подсчитайте количество экземпляров x в списке. Не считайте количество х в любых подсписках в списке.

Я не могу понять, как пропустить подсписок в тестовом списке. Он использует буквы, а не цифры, поэтому я не могу использовать numberp, а когда у меня есть только одна буква, я не могу использовать list-length. Также я не уверен, что делаю let* затем setf правильно, но я не могу заставить его работать по-другому.

(defun count-of (x list)
  (let* count 0)
  (setf count 0)
  (dolist (y list)
    (if (not a sublist) 
        (setf count (+ count 1)))))

(print (count-of 'a '(a '(a c) d c a)))

Ответы [ 2 ]

1 голос
/ 30 сентября 2019

Я не могу понять, как пропустить подсписок в тестовом списке.

Чтобы проверить подсписки, вам нужно будет выполнить more , поэтому задание фактически требует от вас простоты. Учтите, что вы можете сравнивать значения без рекурсии с EQL:

(eql 'a 'b) => NIL
(eql 'a 'a) => T

Но eql не ограничен ни символами, ни числами, вы можете передать любой объект, и он вернетNIL, когда два значения не равны тривиально.

(eql '(a b c) 'a) => NIL

Другими словами, вы увеличиваете свой счетчик, когда находите совпадение с eql, и все.

Heиспользует буквы, а не цифры, поэтому я не могу использовать число p, а когда у меня есть только одна буква, я не могу использовать длину списка.

Если вы хотите различать символы и списки, вы можете использовать предикаты, такие как atom или consp, или даже typecase (см. 14.2 Словарь Conses) ).

Также я не уверен, что я правильно делаю let * затем setf, но я не могу заставить его работать другим способом.

Вы действительно используете let неправильно;LET вводит переменные вокруг тела, например:

(let ((x 10)
      (y 20))
  (+ x y))

Поэтому вам нужно будет сделать:

(let ((count 0))
  (dolist (x list count)
    ...))

Обратите внимание, что DOLIST допускает третий параметр, который представляет собой форму, которая будет оцениваться в конце цикла. Внутри цикла вы можете setf считать как хотите (см. Также incf).

0 голосов
/ 30 сентября 2019

Вместо того, чтобы дать вам ответ, который просто поможет вам сдать домашнее задание, не задумываясь, я постараюсь дать ответ, который поможет вам подумать о проблеме и других подобных проблемах, например хакер-лисп.

Для этого подумайте об алгоритме решения этой проблемы:

Если мы знаем, сколько вхождений того, что мы ищем, в списке на данный момент, то:

  • если список пуст, то ответом является число, которое мы видели до сих пор;
  • в противном случае список не пуст, и -
    • , если первый элементсписок такой же, как и то, что мы рассчитываем, тогда ответ - это количество вхождений в остальной части списка, если предположить, что мы увидели на один больше, чем мы видели ранее;
    • , если первый элементсписок не совпадает с тем, что мы рассчитываем, тогда ответ - это число случаев в остальной части списка, при условии, что мы видели то же число, которое мы уже видели.

И все. Ну, мы можем очень легко превратить это в код: очевидно, это спецификация рекурсивной функции с базовым регистром для рекурсии, когда список пуст. Но чтобы сделать вещи более интересными, мы сильно ограничим части языка, который мы можем использовать. В частности, в тексте определения:

  • функция не может знать свое собственное имя;
  • единственная конструкция связывания переменных, которую мы можем использовать, это lambda.

Первый трюк заключается в том, как использовать lambda для привязки переменных. Ну, это легко, потому что lambda делает связывание переменных:

((lambda (x) ... here x is bound to 1 ...) 1)

Второй трюк состоит в том, что если функции не разрешено знать свое имя, то способ, которым она должна вызыватьсам по себе передается сам в качестве одного из аргументов. Итак, внутренности функции выглядят так:

(lambda (c elt tail count-so-far)
  ;; Here:
  ;; - c is the function we must call to count the rest of the list
  ;;   (it's this function!);
  ;; - elt is the thing we are looking for;
  ;; - tail is the tail of the list we are searching;
  ;; - count-so-far is the count of elements found so far
  ;;
  (if (null tail)
      ;; we're done: count-so-far is the count
      count-so-far
    ;; There is more to do. eql is the appropriate 'is this the same
    ;; as that' predicate in this case.
    (if (eql elt (first tail))
        ;; we've found one, so we need to recurse with the count being one more
        (funcall c c elt (rest tail) (+ count-so-far 1))
      ;; we didn't find one
      (funcall c c elt (rest tail) count-so-far))))

Раздражающая вещь в этом коде состоит в том, что для вызова функции мы должны сделать это (funcall c c ...): причина этого в том, чточто CL - это то, что называется Lisp-2, поэтому в такой форме, как (f x), f ищется в другом пространстве имен (другом пространстве привязок), чем x: funcall - это то, что мы должны использоватьсказать «вызов функции, которую я связал с этой переменной». В любом случае мы вызываем все, что связано с c, передавая его в качестве первого аргумента, чтобы он, в свою очередь, мог вызывать себя.

Теперь мы должны инициировать процесс, связывая эту вещь спеременная, а затем вызвать его. Мы можем сделать это, используя lambda, как указано выше.

((lambda (f)
   ;; kick off the recursion: at the start the count of elements seen
   ;; so far is 0.
   (funcall f f the-element list 0))
 (lambda (c elt tail count-so-far)
   ;; Here:
   ;; - c is the function we must call to count the rest of the list
   ;;   (it's this function!);
   ;; - elt is the thing we are looking for;
   ;; - tail is the tail of the list we are searching;
   ;; - count-so-far is the count of elements found so far
   ;;
   (if (null tail)
       ;; we're done: count-so-far is the count
       count-so-far
     ;; There is more to do. eql is the appropriate 'is this the same
     ;; as that' predicate in this case.
     (if (eql elt (first tail))
         ;; we've found one, so we need to recurse with the count being one more
         (funcall c c elt (rest tail) (+ count-so-far 1))
       ;; we didn't find one
       (funcall c c elt (rest tail) count-so-far)))))

И, наконец, мы можем обернуть это в обычное определение функции, которое мы позволим себе использовать на верхнем уровне:

(defun count-of (the-element list)
  ;; Count the-element in list
  ((lambda (f)
     ;; kick off the recursion: at the start the count of elements
     ;; seen so far is 0.
     (funcall f f the-element list 0))
   (lambda (c elt tail count-so-far)
     ;; Here:
     ;; - c is the function we must call to count the rest of the list
     ;;   (it's this function!);
     ;; - elt is the thing we are looking for;
     ;; - tail is the tail of the list we are searching;
     ;; - count-so-far is the count of elements found so far
     ;;
     (if (null tail)
         ;; we're done: count-so-far is the count
         count-so-far
       ;; There is more to do. eql is the appropriate 'is this the
       ;; same as that' predicate in this case.
       (if (eql elt (first tail))
           ;; we've found one, so we need to recurse with the count
           ;; being one more
           (funcall c c elt (rest tail) (+ count-so-far 1))
         ;; we didn't find one
       (funcall c c elt (rest tail) count-so-far))))))

Вы можете сделать несколько упрощений: рекурсивная функция может просто полагаться на привязки, сделанные во внешней функции, и мы можем слегка вывернуть рекурсивный вызов наизнанку:

(defun count-of (the-element list)
  ;; Count the-element in list
  ((lambda (f)
     ;; kick off the recursion: at the start the count of elements
     ;; seen so far is 0.
     (funcall f f list 0))
   (lambda (c tail count-so-far)
     ;; Here:
     ;; - c is the function we must call to count the rest of the list
     ;;   (it's this function)l
     ;; - tail is the tail of the list we are searching;
     ;; - count-so-far is the count of elements found so far
     ;;
     (if (null tail)
         ;; we're done: count-so-far is the count
         count-so-far
       ;; There is more to do. eql is the appropriate 'is this the
       ;; same as that' predicate in this case.
       (funcall c c (rest tail)
                (+ count-so-far (if (eql the-element (first tail)) 1 0)))))))

Это, однако, не является улучшением: оно немного короче.

И мы можем видеть эту работу:

> (count-of 'a '(1 2 3 4 a (a a a) a 5))
2

> (count-of 'a '(1 2 3 4 (a a a) a 5))
1

> (count-of 'a '(1 2 3 4 (a a a) 5))
0

Для дополнительной информации здесь приведена версия последней версии. выше на языке, который является Lisp-1, поэтому нам не нужно funcall. Это Ракетка :

(define count-of
  (λ (the-element lst)
    ((λ (f)
       (f f lst 0))
     (λ (c tail count-so-far)
       (if (null? tail)
           count-so-far
           (c c (rest tail)
              (+ count-so-far (if (eqv? the-element (first tail)) 1 0))))))))
...