Mam zadanie tworzenia skryptu, który pobiera duży plik tekstowy jako dane wejściowe. Następnie musi znaleźć wszystkie słowa i liczbę wystąpień i utworzyć nowy plik z każdym wierszem wyświetlającym unikalne słowo i jego wystąpienie.Czy można przyspieszyć ten skrypt powłoki?
Jako przykład wziąć plik z tej zawartości:
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum.
trzeba utworzyć plik, który wygląda tak:
1 AD
1 ADIPISICING
1 ALIQUA
...
1 ALIQUIP
1 DO
2 DOLOR
2 DOLORE
...
W tym celu napisałem skrypt, używając tr
, sort
i uniq
:
#!/bin/sh
INPUT=$1
OUTPUT=$2
if [ -a $INPUT ]
then
tr '[:space:][\-_?!.;\:]' '\n' < $INPUT |
tr -d '[:punct:][:special:][:digit:]' |
tr '[:lower:]' '[:upper:]' |
sort |
uniq -c > $OUTPUT
fi
Co to jest? es dzieli słowa za pomocą spacji jako ogranicznika. Jeśli słowo zawiera -_?!.;:
, ponownie je rozbijam na słowa. Usuwam znaki interpunkcyjne, znaki specjalne i cyfry oraz konwertuję cały ciąg znaków na wielkie litery. Po wykonaniu tej czynności sortuję ją i przekazuję ją przez uniq
, aby uzyskać żądany format.
Teraz pobrałem Biblię w formacie txt i użyłem jej jako wejścia. Timing to mam:
scripts|$ time ./text-to-word.sh text.txt b
./text-to-word.sh text.txt b 16.17s user 0.09s system 102% cpu 15.934 total
I zrobił to samo ze skryptu Pythona:
import re
from collections import Counter
from itertools import chain
import sys
file = open(sys.argv[1])
c = Counter()
for line in file.readlines():
c.update([re.sub('[^a-zA-Z]', '', l).upper()
for l in chain(*[re.split('[-_?!.;:]', word)
for word in line.split()])])
file2 = open('output.txt', 'w')
for key in sorted(c):
file2.write(key + ' ' + str(c[key]) + '\n')
Kiedy wykonywany skrypt uzyskałem:
scripts|$ time python text-to-word.py text.txt
python text-to-word.py text.txt 7.23s user 0.04s system 97% cpu 7.456 total
Jak widać zabrakło w 7.23s w porównaniu do skryptu powłoki, który działał w 16.17s. Próbowałem z większymi plikami i zawsze wydaje się, że Python triumfuje. Mam kilka pytań do powyższego senario:
- Dlaczego skrypt Pythona jest szybszy, biorąc pod uwagę, że polecenia powłoki są napisane w języku C? Rozumiem, że skrypt powłoki może nie być optymalny.
- Jak mogę poprawić skrypt powłoki?
- Czy mogę ulepszyć skrypt Pythona?
Aby było jasne, nie porównuję Pythona ze skryptami powłoki. Nie próbuję rozpoczynać wojny płomieniowej lub nie potrzebuję odpowiedzi w jakimkolwiek innym języku, porównując się do bycia szybszym. Używając filozofii UNIXa do wykonywania małych poleceń w celu wykonania zadania, jak mogę przyspieszyć działanie skryptu powłoki?
Sugeruję zmienić tytuł do czegoś podobnego " Czy można zrobić ten skrypt powłoki szybciej? ", Używając tak innego skryptu pythonowego y jako punkt porównania. Wyeliminowałoby to ryzyko bezużytecznych i nietypowych dyskusji na temat różnic między pythonem a powłoką. –
Nie sądzę, że filozofia \ * nix używania wielu małych poleceń, które wykonują jedno zadanie dobrze, jest na miejscu, ponieważ jest najbardziej * wydajna *. Powodem tego jest to, że dzięki naszym narzędziom możesz osiągnąć tak wiele i zaoszczędzić sobie wiele czasu na opracowaniu nowego programu dla prostego zadania. – mgilson
Python jest również zapisany w języku C. Bycie "napisanym w języku C" nie wystarcza, aby wszystko szybko się stało - interweniujące warstwy (i cały odczyt/zapis do i z potoków) mają nad głową. –