Mam plik, który zawiera kilka tysięcy liczb, każda w osobnej linii:
34
42
11
6
2
99
...
Chcę napisać skrypt, który wydrukuje sumę wszystkich liczb w pliku. Mam rozwiązanie, ale nie jest zbyt wydajne. (Uruchomienie zajmuje kilka minut.) Szukam bardziej wydajnego rozwiązania. Jakieś sugestie?
awk
ibc
). Wszystkie zakończyły dodawanie miliona numerów w mniej niż 10 sekund. Spójrz na nie i zobacz, jak można to zrobić w czystej powłoce.Odpowiedzi:
W przypadku liniowej wersji Perla jest to w zasadzie to samo, co
awk
rozwiązanie w odpowiedzi Aymana Hourieha :Jeśli jesteś ciekawy, co robią liniowce Perla, możesz je wyodrębnić:
Rezultatem jest bardziej szczegółowa wersja programu, w formie, której nikt nigdy nie napisałby samodzielnie:
Tylko na chichoty próbowałem tego z plikiem zawierającym 1 000 000 liczb (w zakresie 0 - 9 999). Na moim Macu Pro powraca praktycznie natychmiast. Szkoda, bo miałem nadzieję, że użycie
mmap
będzie naprawdę szybkie, ale to w tym samym czasie:źródło
while { }
pętlę wokół twojego programu. Jeśli włożysz do} ... {
środka, to maszwhile { } ... { }
. Zło? Nieco.-MO=Deparse
opcji! Mimo że na osobny temat.Możesz użyć awk:
źródło
-F '\t'
opcją, jeśli pola zawierają spacje i są oddzielone tabulatorami.Żadne z dotychczasowych rozwiązań nie jest używane
paste
. Tu jest jeden:Jako przykład obliczyć Σn, gdzie 1 <= n <= 100000:
(Dla ciekawskich
seq n
wydrukuje ciąg liczb od1
don
podanej liczby dodatniejn
).źródło
seq 100000 | paste -sd+ - | bc -l
w powłoce Bash Mac OS X. A to zdecydowanie najsłodsze i najbardziej unikalne rozwiązanie!Dla zabawy porównajmy to:
Przerwałem bieg sed po 5 minutach
Nurkowałem do luai jest szybki:
i podczas aktualizacji: ruby:
Posłuchaj porady Eda Mortona: za pomocą
$1
vs używanie
$0
źródło
tr
rozwiązaniem.$0
zamiast tego,$1
ponieważ awk dokonuje podziału pola (co oczywiście zajmuje czas), jeśli jakieś pole jest wyraźnie wymienione w skrypcie, ale nie robi tego inaczej.Inną opcją jest użycie
jq
:-s
(--slurp
) odczytuje linie wejściowe do tablicy.źródło
To jest proste Bash:
źródło
Oto kolejna linijka
Zakłada się, że liczby są liczbami całkowitymi. Jeśli potrzebujesz miejsc po przecinku, spróbuj
Dostosuj 2 do wymaganej liczby miejsc po przecinku.
źródło
Wolę używać GNU datamash do takich zadań, ponieważ jest bardziej zwięzły i czytelny niż perl lub awk. Na przykład
gdzie 1 oznacza pierwszą kolumnę danych.
źródło
źródło
Wolę użyć do tego R:
źródło
(to samo co odpowiedź Briana d Foy'a, bez „END”)
źródło
perl -MO=Deparse
aby zobaczyć, jak perl analizuje program. lub dokumenty dla perlrun: perldoc.perl.org/perlrun.html (wyszukaj -n). perl otacza twój kod {} jeśli użyjesz -n, aby stał się kompletnym programem.Bardziej zwięzłe:
źródło
time python -c "print(sum([float(s) for s in open('random_numbers','r')]))"
Perl 6
źródło
Dla zabawy, zróbmy to dzięki PDL , silnikowi matematycznemu Perla!
rcols
wczytuje kolumny do macierzy (w tym przypadku 1D) isum
(niespodzianka) sumuje cały element macierzy.źródło
Oto rozwiązanie wykorzystujące Python z wyrażeniem generatora. Testowany z milionem numerów na moim starym, grubym laptopie.
źródło
map()
:map(float, sys.stdin)
Nie mogłem tak po prostu przejść ... Oto mój liniowiec Haskell. Jest właściwie całkiem czytelny:
Niestety nie ma
ghci -e
po prostu go uruchomić, więc potrzebuje głównej funkcji, drukowania i kompilacji.Aby to wyjaśnić, odczytujemy cały input (
getContents
), dzielimy nalines
,read
jako liczby isum
.<$>
jestfmap
operatorem - używamy go zamiast zwykłej aplikacji funkcji, ponieważ na pewno wszystko to dzieje się w IO.read
potrzebuje dodatkowegofmap
, ponieważ znajduje się również na liście.Oto dziwne ulepszenie, aby działało z liczbami zmiennoprzecinkowymi:
źródło
źródło
Uruchamianie skryptów R.
Napisałem skrypt R, który pobiera argumenty nazwy pliku i sumuje linie.
Można to przyspieszyć za pomocą pakietu „data.table” lub „vroom” w następujący sposób:
Benchmarking
Te same dane z testów porównawczych jak @glenn jackman .
W porównaniu do powyższego wywołania R, uruchomienie R 3.5.0 jako skryptu jest porównywalne z innymi metodami (na tym samym serwerze Linux Debian).
Skrypt R z readLines
Skrypt R z tabelą danych
Skrypt R z vroom
Porównanie z innymi językami
Dla odniesienia tutaj, jak niektóre inne metody sugerowane na tym samym sprzęcie
Python 2 (2.7.13)
Python 3 (3.6.8)
Rubin (2.3.3)
Perl (5.24.1)
Awk (4.1.4)
C (wersja clang 3.3; gcc (Debian 6.3.0-18) 6.3.0)
Zaktualizuj o dodatkowe języki
Lua (5.3.5)
tr (8.26) musi być mierzony w bash, niekompatybilny z zsh
sed (4.4) musi być mierzony w bash, niekompatybilny z zsh
Uwaga: wywołania sed wydają się działać szybciej w systemach z większą dostępną pamięcią (zwróć uwagę na mniejsze zestawy danych używane do testowania sed)
Julia (0,5.0)
Zauważ, że podobnie jak w R, metody we / wy pliku mają różną wydajność.
źródło
C ++ „one-liner”:
źródło
Kolejna dla zabawy
lub tylko inny bash
Ale rozwiązanie awk jest prawdopodobnie najlepsze, ponieważ jest najbardziej kompaktowe.
źródło
C zawsze wygrywa z prędkością:
Czas dla numerów 1M (ta sama maszyna / dane wejściowe, co moja odpowiedź w pythonie):
źródło
Z Ruby:
źródło
ruby -e'p readlines.map(&:to_f).reduce(:+)'
.Nie wiem, czy możesz uzyskać o wiele lepsze wyniki, biorąc pod uwagę, że musisz przeczytać cały plik.
źródło
$_
jest zmienną domyślną. Operator wprowadzania liniowego<>
domyślnie umieszcza jego wynik, gdy używasz<>
wwhile
.$_
jest zmienną tematu - działa jak „it”. W takim przypadku<>
przypisuje się do niego każdą linię. Wykorzystuje się go w wielu miejscach, aby zmniejszyć bałagan w kodzie i pomóc w pisaniu jednowarstwowych. Skrypt mówi „Ustaw sumę na 0, przeczytaj każdy wiersz i dodaj ją do sumy, a następnie wydrukuj sumę”.$sum
. Ponieważ jest to tak proste, możesz nawet użyć modyfikatora instrukcjiwhile
:$sum += $_ while <>; print $sum;
Nie przetestowałem tego, ale powinno działać:
Być może będziesz musiał dodać „\ n” do ciągu przed bc (jak za pomocą echa), jeśli bc nie traktuje EOF i EOL ...
źródło
bc
wydaje błąd składniowy z powodu końcowego „+” i braku znaku nowej linii na końcu. Działa to i eliminuje bezużyteczne użyciecat
:{ tr "\n" "+" | sed 's/+$/\n/'| bc; } < numbers2.txt
lub<numbers2.txt tr "\n" "+" | sed 's/+$/\n/'| bc
tr "\n" "+" <file | sed 's/+$/\n/' | bc
Oto kolejna:
źródło
Możesz to zrobić za pomocą Alacon - narzędzia wiersza polecenia dla bazy danych Alasql .
Działa z Node.js, więc musisz zainstalować Node.js, a następnie pakiet Alasql :
Aby obliczyć sumę z pliku TXT, możesz użyć następującego polecenia:
źródło
Nie jest łatwiej zastąpić wszystkie nowe wiersze
+
, dodać a0
i wysłać doRuby
tłumacza?Jeśli nie masz
irb
, możesz wysłać go nabc
adres, ale musisz usunąć wszystkie nowe wiersze z wyjątkiem ostatniego (zecho
). Lepiej jest do tego użyćtr
, chyba że masz doktoratsed
.źródło
In Go:
źródło
Wariant Bash
źródło
W powłoce używającej awk użyłem do tego poniższego skryptu:
źródło