2013-07-16 16 views
5

Zasadniczo chciałbym używać mapy, aby zrobić wybór na liście jaknie zwraca nic z LISP/program

(define tbl '(a b c d)) 
(map (lambda (item 'c) (if (eq? item 'c) item (...what in else?)))) 

wynik chcę się

'(c) 

próbowałem opuścić część innego puste, narzeka, że ​​potrzebna jest druga część. Próbowałem

(display "foo") 

jako część innego i mam

(#<void> #<void> C#<void>) 

który jest blisko.

Czy istnieje sposób, w jaki mogę użyć mapy, aby uzyskać "(c)? Znam sposób rekursywny, ale zastanawiam się, czy mapa też może to zrobić. Jeśli nie "(c), przynajmniej (# # C#), ale nie używając hackowania wyświetlania, aby uzyskać wartość zwracaną typu pustego.


+0

Przez sposób: 'lambda' przekazany jako parametr' map' musi otrzymać parametr _single_, jesteś przejazdem dwa i że nie będzie działać: '(pozycja„c) ' –

+0

Jakiego tłumacza używasz? Domyślam się, że rakieta, ale proszę, potwierdź to. –

+1

@ Óscar López, tak, to rakieta, ale mam nadzieję, że rozwiązanie może być ogólne. –

Odpowiedz

12

Chcesz użyć filter, nie map - ponieważ lista wyjściowa będzie potencjalnie mają mniej elementów niż listy wejściowej. Wszystkie te #<void> Wartości zwracane przez display istnieją ponieważ map będzie zawsze zawierać wynik na liście wyjściowej, nawet dla tych elementów nie jesteśmy zainteresowani

(define tbl '(a b c d)) 

(filter (lambda (item) (eq? item 'c)) tbl) 
=> '(c) 

równoważnie i nieco krótsze.

(filter (curry eq? 'c) tbl) 
=> '(c) 

map jest używany, gdy chcesz coś zrobić dla każdego elementu na liście wejściowej, bez odrzucania elementów. Z drugiej strony, filter jest używany do wybierania niektórych elementów na liście wejściowej, tych, które oceniają na #t dla danego predykatu, a filter jest dostępny w większości tłumaczy schematów, jeśli nie jest dostępny, można zaimportować SRFI-1 lub użyć reference implementation.

Nie ma sposobu, aby uzyskać '(c) użyciu tylkomap (może to być posiekany użyciu map plusa apply lub remove* itd, ale nie jest to pomysł, prawda?); jeśli z jakiegoś powodu trzeba użytku map i nie przeszkadza powrocie listę zastępcze, oto kilka alternatyw:

(map (lambda (item) (if (eq? item 'c) item '%)) tbl) ; placeholder in else part 
=> '(% % c %) 

(map (lambda (item) (when (eq? item 'c) item)) tbl) ; when has implicit #<void> 
=> '(#<void> #<void> C#<void>) 

Czas na mały hacking.Korzystanie map powiększonej apply (jak wyjaśniono w odpowiedzi @WillNess'), to ma tę zaletę, że działa w każdej tłumacza RXR i jest najbardziej przenośnym rozwiązaniem, ponieważ wykorzystuje standardowe procedury:

(apply append (map (lambda (item) (if (eq? item 'c) (list item) '())) tbl)) 
=> '(c) 

Korzystanie map plusa remove*:

(remove* (list (void)) (map (lambda (item) (when (eq? item 'c) item)) tbl)) 
=> '(c) 

Dla odmiany, rozwiązanie bez map - używając foldr zamiast:

(foldr (lambda (item a) (append (if (eq? item 'c) (list item) '()) a)) '() tbl) 
=> '(c) 

Oczywiście, zawsze można zaimplementować własną wersję filter używając tylko standardowe procedury, będzie to również przenośny we wszystkich RXR tłumaczy:

(define (filter pred? lst) 
    (cond ((null? lst) 
     '()) 
     ((not (pred? (car lst))) 
     (filter pred? (cdr lst))) 
     (else 
     (cons (car lst) 
       (filter pred? (cdr lst)))))) 

(filter (lambda (item) (eq? item 'c)) tbl) 
=> '(c) 
+0

Zalecana lektura dla lepszego zrozumienia procedur 'map' i' filter': [Sekwencje jako konwencjonalne interfejsy] (https://mitpress.mit.edu/ sicp/full-text/book/book-ZH-15.html #% _ sec_2.2.3) w [SICP] (https://mitpress.mit.edu/sicp/full-text/book/book.html) –

+1

An alternatywa to (filter (cut eq? 'c <>) tbl), jeśli twoja implementacja nie ma funkcji curry, ale obsługuje SRFI-26. A dla każdego, kto chce kopać głębiej, zarówno mapę, jak i filtr można zaimplementować za pomocą polecenia fold. –

+0

'# ': jeszcze jeden odłam 'nil', jak złamany w Schemacie. Co to jest klaster. "Jakoś wolałem (CDR (ASEY KLUCZA A-LIST))" - Ashwin Ram – Kaz

1

Nie wspominając o wersji Schemat/Środowisko. Zakładając, że masz tylko najbardziej podstawowego systemu, jest to dość łatwe do wdrożenia coś:

(define (choose-if pred list) 
    (let choosing ((list list) (rslt '())) 
    (if (null? list) 
     (reverse rslt) 
     (choosing (cdr list) 
        (if (pred (car list)) 
         (cons (car list) rslt) 
         rslt))))) 

a następnie powiązane:

(define (choose item list) 
    (choose-if (lambda (elt) (eq? item elt)) list)) 

(define (choose-if-not pred list) 
    (choose-if (lambda (elt) (not (pred elt))) list)) 

i zastosowanie:

> (choose 'c '(a b c d)) 
(c) 

Masz również opcję używać prostych elementów podstawowych, takich jak:

(define (choose item list) 
    (remq #f (map (lambda (elt) (eq? item elt)) list))) 

lub

(define (choose item list) 
    (remp (lambda (elt) (not (eq? item elt))) list)) 
3

Prosta "standard" Sztuką jest

(apply append (map (lambda(x)(if (eq? x 'c) (list x) '())) '(a b c d))) 
;Value 12: (c) 

która zwraca (c). apply append ... map kombi jest znany jako mapcan w Common Lisp ("mapcan" za "map i złączyć"):

[1]> (mapcan #'(lambda(x)(if (eq x 'c) (list x))) '(a b c d)) 
(C) 

Schemat MIT ma tej funkcji też.

apply append spłaszcza jeden poziom swojej liście argumentów, (apply append '((a)() (c)()))==(append '(a) '() '(c) '())->(a c). Ponieważ puste listy znikają, jest to przydatne do eliminowania elementów z map. Osiąga taki sam efekt jak filter, jeśli masz jeden dostępny (nie jest w R5RS, ale jest w SRFI1).

Może być stosowany dla innych skutków zbyt, jak podwojenie:

[2]> (mapcan #'(lambda(x)(if (evenp x) (list x x))) '(1 2 3 4)) 
(2 2 4 4) 

Tak na marginesie, mapcan to co jest znane jako „wiążą” List monada jest (z argumentami odwrót) i (apply append ...) jako jego operacja "dołącz". Rzeczywiście tak musi być dla każdej monady, dla list też bind m f == join (map f m).

Jest to również podstawa dla np. Haskell jest listowych:

Prelude> [y | x<-[1,2,3,4], even x, y<-[x,x]] 
[2,2,4,4] 
+0

Ekwiwalent rakietowy 'mapcan' to' append-map'. –

Powiązane problemy