Jak już wspomniano, vapply
robi dwie rzeczy:
- Nieznaczna poprawa prędkości
- Poprawia spójność, zapewniając ograniczone kontrole typu powrotu.
Druga kwestia to większa zaleta, ponieważ pomaga wychwycić błędy, zanim się pojawią, i prowadzi do bardziej niezawodnego kodu. To sprawdzanie wartości zwracanej można przeprowadzić osobno, używając, sapply
a następnie, stopifnot
aby upewnić się, że wartości zwracane są zgodne z oczekiwanymi, ale vapply
jest trochę łatwiejsze (jeśli jest bardziej ograniczone, ponieważ niestandardowy kod sprawdzania błędów może sprawdzać wartości w granicach itp.) ).
Oto przykład vapply
upewnienia się, że wynik jest zgodny z oczekiwaniami. Jest to analogia do czegoś, nad czym właśnie pracowałem podczas skrobania plików PDF, gdzie findD
użyłbym plikuwyrażenie regularneaby dopasować wzorzec w nieprzetworzonych danych tekstowych (np. miałbym listę split
według encji i wyrażenie regularne do dopasowania adresów w każdej encji. Czasami plik PDF był konwertowany poza kolejnością i były dwa adresy dla podmiot, który spowodował zło).
> input1 <- list( letters[1:5], letters[3:12], letters[c(5,2,4,7,1)] )
> input2 <- list( letters[1:5], letters[3:12], letters[c(2,5,4,7,15,4)] )
> findD <- function(x) x[x=="d"]
> sapply(input1, findD )
[1] "d" "d" "d"
> sapply(input2, findD )
[[1]]
[1] "d"
[[2]]
[1] "d"
[[3]]
[1] "d" "d"
> vapply(input1, findD, "" )
[1] "d" "d" "d"
> vapply(input2, findD, "" )
Error in vapply(input2, findD, "") : values must be length 1,
but FUN(X[[3]]) result is length 2
Jak mówię moim studentom, częścią stania się programistą jest zmiana sposobu myślenia z „błędy są irytujące” na „błędy są moim przyjacielem”.
Wejścia o zerowej długości
Jedną z powiązanych kwestii jest to, że jeśli długość danych wejściowych wynosi zero, sapply
zawsze zwróci pustą listę, niezależnie od typu wejścia. Porównać:
sapply(1:5, identity)
sapply(integer(), identity)
vapply(1:5, identity)
vapply(integer(), identity)
Dzięki temu vapply
masz gwarancję określonego typu danych wyjściowych, więc nie musisz pisać dodatkowych sprawdzeń dla danych wejściowych o zerowej długości.
Benchmarki
vapply
może być nieco szybszy, ponieważ już wie, w jakim formacie powinien oczekiwać wyników.
input1.long <- rep(input1,10000)
library(microbenchmark)
m <- microbenchmark(
sapply(input1.long, findD ),
vapply(input1.long, findD, "" )
)
library(ggplot2)
library(taRifx)
autoplot(m)