Krótkie tło: Wiele (najbardziej?) Współczesnych języków programowania w powszechnym użyciu ma co najmniej garstkę ADT [abstrakcyjnych typów danych], w szczególności:
ciąg (sekwencja złożona ze znaków)
lista (uporządkowany zbiór wartości) oraz
typ oparty na mapie (nieuporządkowana tablica odwzorowująca klucze na wartości)
W języku programowania R pierwsze dwa są realizowane odpowiednio jako character
i vector
.
Kiedy zacząłem uczyć się R, dwie rzeczy były oczywiste prawie od samego początku: list
jest to najważniejszy typ danych w R (ponieważ jest to klasa nadrzędna dla R data.frame
), a po drugie, po prostu nie mogłem zrozumieć, w jaki sposób działały nie wystarczająco dobrze, aby używać ich poprawnie w moim kodzie.
Po pierwsze, wydawało mi się, że list
typ danych R był prostą implementacją mapy ADT ( dictionary
w Pythonie, NSMutableDictionary
w Celu C, hash
w Perlu i Ruby, object literal
w Javascript i tak dalej).
Na przykład, tworzysz je tak jak w słowniku Pythona, przekazując pary klucz-wartość do konstruktora (co dict
nie jest w Pythonie list
):
x = list("ev1"=10, "ev2"=15, "rv"="Group 1")
I uzyskujesz dostęp do elementów Listy R, tak jak do słownika Python, np x['ev1']
. Podobnie można pobrać tylko „klucze” lub tylko „wartości” poprzez:
names(x) # fetch just the 'keys' of an R list
# [1] "ev1" "ev2" "rv"
unlist(x) # fetch just the 'values' of an R list
# ev1 ev2 rv
# "10" "15" "Group 1"
x = list("a"=6, "b"=9, "c"=3)
sum(unlist(x))
# [1] 18
ale R list
są również inne niż inne ADT typu mapowego (spośród języków, których i tak się nauczyłem). Domyślam się, że jest to konsekwencja początkowej specyfikacji S, tj. Zamiaru zaprojektowania od podstaw danych DSL danych / statystyk [język specyficzny dla domeny].
trzy znaczące różnice między R list
i typami odwzorowań w innych językach w powszechnym użyciu (np. Python, Perl, JavaScript):
po pierwsze , list
s w R są kolekcją uporządkowaną , podobnie jak wektory, nawet jeśli wartości są kluczowane (tzn. klucze mogą być dowolnymi wartościami mieszającymi, a nie tylko kolejnymi liczbami całkowitymi). Prawie zawsze typ danych mapowania w innych językach jest nieuporządkowany .
po drugie , list
s mogą zostać zwrócone z funkcji, nawet jeśli nigdy nie przeszedłeś, list
kiedy wywołałeś funkcję, i nawet jeśli funkcja, która zwróciła list
nie zawiera (jawnego) list
konstruktora (Oczywiście możesz sobie z tym poradzić poprzez: zawijanie zwróconego wyniku w wywołanie do unlist
):
x = strsplit(LETTERS[1:10], "") # passing in an object of type 'character'
class(x) # returns 'list', not a vector of length 2
# [1] list
Trzecia cecha szczególna od R: list
s: to nie wydaje się, że mogą być członkami innego ADT, a jeśli staramy się robić to pierwotny pojemnik jest zmuszany do list
. Na przykład,
x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=TRUE)
class(x)
# [1] list
nie zamierzam tu krytykować języka ani sposobu jego dokumentowania; podobnie nie sugeruję, że coś jest nie tak ze list
strukturą danych lub z jej zachowaniem. Chcę tylko poprawić, rozumiem, w jaki sposób działają, dzięki czemu mogę poprawnie używać ich w kodzie.
Oto rzeczy, które chciałbym lepiej zrozumieć:
Jakie reguły określają, kiedy wywołanie funkcji zwróci
list
(np.strsplit
Wyrażenie wyszczególnione powyżej)?Jeśli nie przypisuję jawnie nazw do
list
(np.list(10,20,30,40)
), Czy domyślne nazwy są tylko liczbami całkowitymi zaczynającymi się od 1? (Zakładam, ale nie jestem pewien, czy odpowiedź brzmi tak, w przeciwnym razie nie bylibyśmy w stanie zmusić tego typulist
do wektora z wywołaniemunlist
.)Dlaczego te dwa różne podmioty,
[]
i[[]]
przywróć sam wynik?x = list(1, 2, 3, 4)
oba wyrażenia zwracają „1”:
x[1]
x[[1]]
dlaczego te dwa wyrażenia nie zwracają tego samego wyniku?
x = list(1, 2, 3, 4)
x2 = list(1:4)
Proszę nie kierować mnie do Dokumentacji R ( ?list
, R-intro
) - Przeczytałem ją uważnie i nie pomaga mi to w odpowiedzi na pytania, które przytoczyłem powyżej.
(na koniec niedawno się nauczyłem i zacząłem używać pakietu R (dostępnego w CRAN), hash
który implementuje konwencjonalne zachowanie typu mapy za pośrednictwem klasy S4; z pewnością mogę polecić ten pakiet.)
list
w R nie jest jak skrót. Mam jeszcze jedną, którą uważam za wartą odnotowania. list
w R może mieć dwóch członków o tej samej nazwie odniesienia. Zastanów się, że obj <- c(list(a=1),list(a=2))
jest poprawny i zwraca listę z dwiema nazwanymi wartościami „a”. W tym przypadku wywołanie for obj["a"]
zwróci tylko pierwszy pasujący element listy. Możesz uzyskać zachowanie podobne (może identyczne) do skrótu z tylko jednym elementem na nazwy, do których się odwołujesz, używając środowisk w R. np.x <- new.env(); x[["a"]] <- 1; x[["a"]] <- 2; x[["a"]]
x = list(1, 2, 3, 4)
, oba NIE zwracają tego samego wyniku:,x[1]
ix[[1]]
. Pierwszy zwraca listę, a drugi zwraca wektor numeryczny. Przewijając poniżej, wydaje mi się, że Dirk był jedynym respondentem, który poprawnie odpowiedział na to pytanie.