2013-12-11 2 views
0

Я хочу создать функцию, которая проверяет, является ли элемент членом списка. Список может содержать другие списки. Это то, что я пришел с до сих пор:Common Lisp: Функция, которая проверяет, является ли элемент членом списка

(defun subl(l) 
    (if (numberp l) 
     (if (= l 10) 
      (princ "Found")) 
     (mapcar 'subl l))) 

Сейчас число Я ищу жестко закодировано и это 10. Я хотел бы написать это как-то так, что функция принимает другой параметр (число, которое я ищу), и возвращает true или 1, когда он его найдет. Основная проблема заключается в том, что я не вижу способа управления mapcar. mapcar выполняет subl на каждый элемент l, если l si список. Но как я могу контролировать возвращаемые значения каждого вызова?

Я хотел бы, чтобы проверить возвращаемое значение каждого subl вызова, и если одна из него является true или 1 вернуть true или 1 до последнего рекурсивного вызова. Поэтому в конце subl возвращает true или one, если элемент содержится в списке или nil в противном случае.

Любая идея?

ответ

1

Эта процедура должна обрабатываться, как вы описали;

(defun member-nested (el l)"whether el is a member of l, el can be atom or cons, 
l can be list of atoms or not" 
     (cond 
     ((null l) nil) 
     ((equal el (car l)) t) 
     ((consp (car l)) (or (member-nested el (car l)) 
          (member-nested el (cdr l)))) 
     (t (member-nested el (cdr l))))) 
+0

Основной проблемой при решении задач, требующих такого мышления и написания алгоритмов, является исчерпание повторяющегося в «машине» первого столкновения вложенный вложенный элемент вложенного элемента ... и т. д. управление копается в нем, и вы заканчиваете с терминатором nil первого вложенного элемента первого вложенного элемента ... и так далее. nil терминатор-I. Чтобы преодолеть это, я применил «или». аналогично делается и приходит в голову быстрее, поскольку «cons», поскольку это более естественная функция, в алгоритмах написания говорят о встречах «равных» элементов или конкретных подсписках исходного. –

2

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

+0

Благодарим за ваш ответ! +1 Я действительно искал что-то вроде этого и не мог найти. Это действительно полезная функция, но, к сожалению, я вынужден использовать «карту» для дидактических причин :( –

+0

Если вы должны использовать 'mapcar', рассмотрите сопоставление предиката, который преобразует список в другой список с результатами этого предиката, а затем он должно быть тривиально, чтобы создать свою собственную версию 'reduce' (если вы не можете использовать встроенную версию), чтобы проверить, верн ли какой-либо из предикатов возвращен true. – yan

+0

Хорошая точка, я попробую. я избавляюсь от жестко закодированного '10', учитывая тот факт, что функции, которые нужно передать на« mapcar », вынуждены иметь только один параметр? –

2

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

Так представьте, что вы разделили два:

;; a predicate to check if an element is 10 
(defun number10p (l) 
    (and (numberp l) 
     (= l 10))) 

;; the utility function to search for 10 amongst elements 
(defun sublistp (haystack) 
    (mapcar #'number10p haystack))) 

Но здесь, когда вы делаете (sublistp '(5 10 15 20)) вы получите (nil t nil nil) обратно. То потому что mapcar делает список каждого результата. Для меня, похоже, вы описываете some, так как он останавливается при первом истинном значении.

(defun sublistp (haystack) 
    (some #'number10p haystack))) 

(sublistp '(5 10 15 20)) ; ==> t 

Теперь, чтобы сделать его работу для любого типа данных мы изменяем предикат и сделать его в качестве локальной функции, где мы имеем аргумент мы ищем:

(defun sublistp (needle haystack) 
    (flet ((needlep (x) 
      (equal x needle))) 
    (some #'needlep haystack))) 

(sublistp '(a b) '(a b c (a b) d e f)) ; ==> t 

Вы также можете сделать это с анонимный предикат, как это:

(defun sublistp (needle haystack) 
    (some #'(lambda (x) 
      (equal x needle)) 
     haystack)) 

реализация этого является member функцией, за исключением того, что возвращает матч в качестве значения истинности.Это нормально, так как ничего, кроме nil верно в CL:

(member 10 '(5 10 15 20)) ; ==> (10 15 20) 

EDIT Вы прокомментировали другой ответ, который вы обязаны использовать mapcar в этом случае использовать его вместе с append, чтобы получить список всех матчей и проверить, если список имеет больше 0 элементов:

(defun sublistp (needle haystack) 
    (flet ((needle-check (x) 
      (if (equal x needle) '(t) nil))) 
    (< 0 (length 
      (apply #'append 
       (mapcar #'needle-check haystack)))))) 

Как это работает в том, что для каждого матча вы получите список из одного элемента и для каждого, не матча вы получите пустой список. При добавлении списков вы получите пустой список, если нет совпадения. Для всех других результатов у вас есть совпадение. Это не очень эффективная реализация.

Смежные вопросы