Tot spun că, cu cât cunoști mai multe limbaje de programare, cu atât le vei înțelege mai mult pe toate celelalte pe care le cunoști – acum sunt în punctul în care vreau să rezolv fiecare problemă pe care o văd într-o mână de limbi diferite. Toate oferă funcționalități diferite, iar unele sunt cu siguranță mai potrivite pentru anumite probleme decât altele, dar există o lume de diferență între două caractere și importul din două biblioteci.
Un buletin informativ pe care îl urmăresc (și nu găsesc copii online) care demonstrează lucruri bune în python (trebuie să-l învăț, deși nu-l iubesc) a fost acoperit recent
accumulate
arătând că sum
şi accumulate
erau oarecum înrudite
>>> my_list = (42, 73, 0, 16, 10) >>> sum(my_list) 141 >>> from itertools import accumulate >>> list(accumulate(my_list)) (42, 115, 115, 131, 141)
sum
adună toate elementele listei, în timp ce accumulate
face același lucru, dar păstrează fiecare sumă parțială succesivă.
Acesta completează demonstrația cu o funcție alternativă utilizată în accumulate
>>> from itertools import accumulate >>> from operator import mul # mul(2, 3) == 6 >>> initial_investment = 1000 >>> rates = (1.01, 1.01, 1.02, 1.025, 1.035, 1.035, 1.06) >>> list( ... accumulate(rates, mul, initial=initial_investment) ... ) (1000, 1010.0, 1020.1, 1040.502, 1066.515, 1103.843, 1142.477, 1211.026)
Acum, în primul rând… from operator import mul
??? Se pare că nu ai cum să treci *
ca argument pentru o funcție. eu putea definiți o funcție care efectuează același lucru pe argumente cunoscute, de ex lambda x, y: x * y
>>> list(accumulate(rates, lambda x, y: x*y, initial=initial_investment)) (1000, 1010.0, 1020.1, 1040.502, 1066.5145499999999, 1103.8425592499998, 1142.4770488237498, 1211.0256717531747)
dar… ew.
Este posibil să existe o modalitate diferită de a aborda asta. Îmi vine în minte o listă de înțelegere, de exemplu ceva de genul
>>> (sum(my_list(0:i)) for i in range(1, len(my_list)+1)) (42, 115, 115, 131, 141)
dar asta necesită efectuarea unei sume pentru fiecare sub-interval, astfel încât performanța nu s-ar scala bine (desigur, asta nu a fost deloc luat în considerare aici). Nici eu nu cred că există un încorporat prod
deci trebuie import math
pentru a face similar
>>> import math >>> x = (initial_investment) + rates >>> (math.prod(x(0:i)) for i in range(1, len(x)+1)) (1000, 1010.0, 1020.1, 1040.502, 1066.5145499999999, 1103.8425592499998, 1142.4770488237498, 1211.0256717531747)
În R care ar putea folosi încorporat cumprod
pentru produsul cumulat
initial_investment <- 1000 rates = c(1.01, 1.01, 1.02, 1.025, 1.035, 1.035, 1.06) cumprod(c(initial_investment, rates)) ## (1) 1000.000 1010.000 1020.100 1040.502 1066.515 1103.843 1142.477 1211.026
dar care are operația de „multiplicare” codificată. cumsum
utilizări +
ca funcția… hmmm. Poate că R nu are un generalizat accumulate
?
M-am jucat cu Haskell în ultima vreme, așa că funcțiile recursive la salvare! O caracteristică a funcțiilor recursive din R care îmi place foarte mult este Recall
care apelează funcția în care este definit cu un nou set de argumente – perfect pentru recursivitate!
accumulate_recall <- function(x, f, i=x(1)) { if (!length(x)) return(NULL) c(i, Recall(tail(x, -1), f, f(i, x(2)))) }
Este, de asemenea, robust împotriva redenumirea funcției; corpul de fapt nu sună
accumulate_recall
pe nume deloc.
Acest lucru ar putea fi ineficient, totuși – nu este neobișnuit să aruncați în aer stiva, deci o nouă Tailcall
funcția (care nu are aceeași eleganță de a fi robustă împotriva redenumiri) ajută la semnalarea acestui lucru ca ceva care poate fi optimizat
accumulate <- function(x, f, i=x(1)) { if (!length(x)) return(NULL) c(i, Tailcall(accumulate, tail(x, -1), f, f(i, x(2)))) }
Cu aceasta, pot emula cumsum()
şi cumprod()
funcții
cumprod(1:6) ## (1) 1 2 6 24 120 720 accumulate(1:6, `*`) ## (1) 1 2 6 24 120 720 cumsum(2:6) ## (1) 2 5 9 14 20 accumulate(2:6, `+`) ## (1) 2 5 9 14 20
dacă nu încerc să calculez ceva prea mare…
cumprod(5:15) ## (1) 5 30 210 1680 15120 151200 ## (7) 1663200 19958400 259459200 3632428800 54486432000 accumulate(5:15, `*`) ## Warning in f(i, x(2)): NAs produced by integer overflow ## (1) 5 30 210 1680 15120 151200 1663200 ## (8) 19958400 259459200 NA NA
Se pare că funcțiile încorporate se convertesc în numere. Acest lucru se rezolvă ușor la intrare
accumulate(as.numeric(5:15), `*`) ## (1) 5 30 210 1680 15120 151200 ## (7) 1663200 19958400 259459200 3632428800 54486432000
În orice caz, există o generalizare accumulate
care ia funcțiile goale drept argumente.
Dar poate fi mult mai curat decât asta!
În APL nu veți găsi nicio funcție numită „sumă” deoarece este doar o reducere (Reduce
în R) cu funcţia +
sum←+/ sum ⍳6 ⍝ sum the values 1:6 21 sum 1↓⍳6 ⍝ sum the values 2:6 20
care în R este
sum(1:6) ## (1) 21 sum(2:6) ## (1) 20
De ce ai scrie sum
doar dacă poți folosi +/
? Sunt mai puține caractere pentru a scrie implementarea decât numele!
Pentru accumulate
terminologia din APL este scan
care folosește un glif foarte asemănător deoarece operația în sine este foarte asemănătoare; o reduce
(/
) este doar ultima valoare a lui a scan
() care păstrează valorile progresive. În ambele cazuri, operatorul (fie bară oblică) ia o funcție binară ca argument din stânga și produce o funcție modificată – în aceste exemple, efectiv
sum
şi prod
– care se aplică apoi valorilor din dreapta. The scan
versiunea face la fel
+⍳6 1 3 6 10 15 21 ×⍳6 1 2 6 24 120 720 accumulate(1:6, `+`) ## (1) 1 3 6 10 15 21 accumulate(1:6, `*`) ## (1) 1 2 6 24 120 720
În ceea ce privește exemplul de rate de mai sus, concatenăm valoarea inițială cu catenate
(,
) la fel ca exemplul R, dar altfel funcționează bine
rates ← 1.01 1.01 1.02 1.025 1.035 1.035 1.06 inv ← 1000 ×/inv, rates 1211.025672 ×inv, rates 1000 1010 1020.1 1040.502 1066.51455 1103.842559 1142.477049 1211.025672
Deci tot acel cod R recursiv făcut pentru a generaliza aplicarea cumulativă a unei funcții furnizate ca argument se rezumă doar la un singur glif . Remarcabil!
Mai mult, există multe de funcții binare cu care s-ar folosi acest lucru, toate având nume scrise în alte limbi
+/ ⍝ sum (add) ×/ ⍝ prod (multiply) ∧/ ⍝ all (and) ∨/ ⍝ any (or) ⌈/ ⍝ maximum (max) ⌊/ ⍝ minimum (min)
În rezumat, se pare că, privind în aceste limbi, opțiunile disponibile variază de la un singur glif pentru scan
împreună cu operatorul binar simplu, de ex
×/
; o cumprod()
funcție care nu este bine generalizată, dar funcționează imediat; și apoi există orice mizerie ar fi aceasta (odată ce ai făcut instalat aceste)
>>> from itertools import accumulate >>> from operator import mul >>> list(accumulate(rates, mul, initial=initial_investment))
Unde am greșit așa?
Pentru cât valorează, Julia are un reduce
si un accumulate
care se comportă foarte frumos; generalizat pentru funcţia binară ca argument
julia> reduce(+, 1:6) 21 julia> reduce(*, 1:6) 720 julia> accumulate(+, 1:6) 6-element Vector{Int64}: 1 3 6 10 15 21 julia> accumulate(*, 1:6) 6-element Vector{Int64}: 1 2 6 24 120 720
Acest lucru este extrem de aproape de abordarea APL, dar cu nume mai lungi pentru reduce
şi scan
operatori. De asemenea, definește cel mai convenabil sum
,
prod
, cumsum
și cumprod
; nu lipsesc modalitățile de a face asta în Julia!
În Haskell, foldl
şi scanl
sunt versiunea (stânga-asociativă) a reduce
şi accumulate
iar trecerea unui infix ca argument necesită împachetarea lui între paranteze
ghci> foldl (+) 0 (1..6) 21 ghci> scanl (+) 0 (1..6) (0,1,3,6,10,15,21) ghci> foldl (*) 1 (1..6) 720 ghci> scanl (*) 1 (1..6) (1,1,2,6,24,120,720)
Aceasta necesită o valoare de pornire explicită, cu excepția cazului în care se utilizează versiunile specializate care folosesc prima valoare ca valoare inițială
ghci> foldl1 (+) (1..6) 21 ghci> scanl1 (+) (1..6) (1,3,6,10,15,21) ghci> foldl1 (*) (1..6) 720 ghci> scanl1 (*) (1..6) (1,2,6,24,120,720)
Am început această postare sperând să demonstrez cât de plăcută a fost sintaxa APL pentru asta, dar ocolul prin generalizarea funcției R a fost și o distracție neașteptată.
Comentariile, îmbunătățirile sau propriile soluții sunt binevenite. Pot fi găsit pe Mastodon sau pot folosi comentariile de mai jos.
Anexe
Probabil trebuie remarcat faptul că R face au o funcție scan
dar este pentru citirea datelor într-un vector – dacă observați vreodată pe cineva care îl folosește pentru asta… rulați. Am povești de război despre această funcție.
Mi-ar plăcea să aud cum se realizează acest lucru și în alte limbi – are încorporat accumulate
care necesită o funcție binară?
devtools::session_info()
## ─ Session info ─────────────────────────────────────────────────────────────── ## setting value ## version R version 4.4.1 (2024-06-14) ## os macOS Sonoma 14.6 ## system aarch64, darwin20 ## ui X11 ## language (EN) ## collate en_US.UTF-8 ## ctype en_US.UTF-8 ## tz Australia/Adelaide ## date 2024-11-28 ## pandoc 3.5 @ /opt/homebrew/bin/ (via rmarkdown) ## ## ─ Packages ─────────────────────────────────────────────────────────────────── ## package * version date (UTC) lib source ## blogdown 1.19 2024-02-01 (1) CRAN (R 4.4.0) ## bookdown 0.41 2024-10-16 (1) CRAN (R 4.4.1) ## bslib 0.8.0 2024-07-29 (1) CRAN (R 4.4.0) ## cachem 1.1.0 2024-05-16 (1) CRAN (R 4.4.0) ## cli 3.6.3 2024-06-21 (1) CRAN (R 4.4.0) ## devtools 2.4.5 2022-10-11 (1) CRAN (R 4.4.0) ## digest 0.6.37 2024-08-19 (1) CRAN (R 4.4.1) ## ellipsis 0.3.2 2021-04-29 (1) CRAN (R 4.4.0) ## evaluate 1.0.1 2024-10-10 (1) CRAN (R 4.4.1) ## fastmap 1.2.0 2024-05-15 (1) CRAN (R 4.4.0) ## fs 1.6.5 2024-10-30 (1) CRAN (R 4.4.1) ## glue 1.8.0 2024-09-30 (1) CRAN (R 4.4.1) ## htmltools 0.5.8.1 2024-04-04 (1) CRAN (R 4.4.0) ## htmlwidgets 1.6.4 2023-12-06 (1) CRAN (R 4.4.0) ## httpuv 1.6.15 2024-03-26 (1) CRAN (R 4.4.0) ## jquerylib 0.1.4 2021-04-26 (1) CRAN (R 4.4.0) ## jsonlite 1.8.9 2024-09-20 (1) CRAN (R 4.4.1) ## knitr 1.48 2024-07-07 (1) CRAN (R 4.4.0) ## later 1.3.2 2023-12-06 (1) CRAN (R 4.4.0) ## lifecycle 1.0.4 2023-11-07 (1) CRAN (R 4.4.0) ## magrittr 2.0.3 2022-03-30 (1) CRAN (R 4.4.0) ## memoise 2.0.1 2021-11-26 (1) CRAN (R 4.4.0) ## mime 0.12 2021-09-28 (1) CRAN (R 4.4.0) ## miniUI 0.1.1.1 2018-05-18 (1) CRAN (R 4.4.0) ## pkgbuild 1.4.5 2024-10-28 (1) CRAN (R 4.4.1) ## pkgload 1.4.0 2024-06-28 (1) CRAN (R 4.4.0) ## profvis 0.4.0 2024-09-20 (1) CRAN (R 4.4.1) ## promises 1.3.0 2024-04-05 (1) CRAN (R 4.4.0) ## purrr 1.0.2 2023-08-10 (1) CRAN (R 4.4.0) ## R6 2.5.1 2021-08-19 (1) CRAN (R 4.4.0) ## Rcpp 1.0.13-1 2024-11-02 (1) CRAN (R 4.4.1) ## remotes 2.5.0.9000 2024-11-03 (1) Github (r-lib/remotes@5b7eb08) ## rlang 1.1.4 2024-06-04 (1) CRAN (R 4.4.0) ## rmarkdown 2.28 2024-08-17 (1) CRAN (R 4.4.0) ## rstudioapi 0.17.1 2024-10-22 (1) CRAN (R 4.4.1) ## sass 0.4.9 2024-03-15 (1) CRAN (R 4.4.0) ## sessioninfo 1.2.2 2021-12-06 (1) CRAN (R 4.4.0) ## shiny 1.9.1 2024-08-01 (1) CRAN (R 4.4.0) ## urlchecker 1.0.1 2021-11-30 (1) CRAN (R 4.4.0) ## usethis 3.0.0 2024-07-29 (1) CRAN (R 4.4.0) ## vctrs 0.6.5 2023-12-01 (1) CRAN (R 4.4.0) ## xfun 0.49 2024-10-31 (1) CRAN (R 4.4.1) ## xtable 1.8-4 2019-04-21 (1) CRAN (R 4.4.0) ## yaml 2.3.10 2024-07-26 (1) CRAN (R 4.4.0) ## ## (1) /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library ## ## ──────────────────────────────────────────────────────────────────────────────