Możesz porównać tylko dwie liczby z dc
podobnymi:
dc -e "[$1]sM $2d $1<Mp"
... gdzie "$1"
jest twoja maksymalna wartość i "$2"
liczba, którą wydrukowałbyś, gdyby była mniejsza niż "$1"
. To także wymaga GNU dc
- ale możesz zrobić to samo, przenośnie:
dc <<MAX
[$1]sM $2d $1<Mp
MAX
W obu powyższych przypadkach możesz ustawić dokładność na wartość inną niż 0 (domyślnie), np ${desired_precision}k
. W obu przypadkach konieczne jest również sprawdzenie, czy obie wartości są zdecydowanie liczbami, ponieważ dc
mogą nawiązywać system()
połączenia z !
operatorem.
Za pomocą następującego małego skryptu (i następnego) powinieneś również zweryfikować dane wejściowe - grep -v \!|dc
lub coś w celu solidnej obsługi dowolnych danych wejściowych. Powinieneś także wiedzieć, że dc
interpretuje liczby ujemne z _
przedrostkiem, a nie -
przedrostkiem - ponieważ ten ostatni jest operatorem odejmowania.
Poza tym, ten skrypt dc
odczyta tyle kolejnych \n
liczb oddzielonych ewline, ile chciałbyś go podać, i wydrukuje dla każdej $max
wartości lub danych wejściowych, w zależności od tego, która z nich jest mniejsza:
dc -e "${max}sm
[ z 0=? d lm<M p s0 lTx ]ST
[ ? z 0!=T q ]S?
[ s0 lm ]SM lTx"
Więc ... każda z tych [
kwadratowych nawiasach ]
połacie jest dc
ciąg obiekt, który jest S
aved każdy z odpowiedniej tablicy - każdy jeden T
, ?
albo M
. Poza kilkoma innymi rzeczami, które dc
mogą mieć związek z łańcuchem , może również x
działać jako makro. Jeśli dobrze to uporządkujesz, w pełni funkcjonalny mały dc
skrypt jest składany w prosty sposób.
dc
działa na stosie . Wszystkie obiekty wejściowe są układane jeden na drugim - każdy nowy obiekt wejściowy popycha ostatni górny obiekt i wszystkie obiekty poniżej niego na stosie o jeden podczas dodawania. Większość odwołań do obiektu odnosi się do górnej wartości stosu, a większość odnośników wyskakuje na górze stosu (co powoduje pociągnięcie wszystkich obiektów pod nim o jeden) .
Oprócz głównego stosu istnieje również (co najmniej) 256 tablic, a każdy element tablicy ma własny stos. Nie używam dużo tego tutaj. Po prostu przechowuję ciągi, jak wspomniano, więc mogę l
je x
przesadzić, kiedy chcę i warunkowo je wyrównać, i s
podarłem $max
wartość w górnej części m
tablicy.
W każdym razie ta niewielka część dc
robi w dużej mierze to, co robi twój skrypt powłoki. Używa opcji GNU-ism -e
- jak dc
zwykle bierze swoje parametry ze standardowego wejścia - ale możesz zrobić to samo:
echo "$script" | cat - /dev/tty | dc
... gdyby $script
wyglądało jak wyżej.
Działa jak:
lTx
- To l
przysiada i x
wylicza makro zapisane na górze T
(chyba do testu - zwykle wybieram te nazwy arbitralnie) .
z 0=?
- T
est następnie sprawdza głębokość stosu w /, z
a jeśli stos jest pusty (odczyt: zawiera 0 obiektów) , wywołuje ?
makro.
? z0!=T q
- ?
Makro jest nazwane dla ?
dc
wbudowanego polecenia, które odczytuje wiersz wejścia ze standardowego wejścia, ale dodałem z
do niego również kolejny test głębokości stosu, aby mógł korzystać q
z całego małego programu, jeśli wciągnie pusty wiersz lub uderzy w EOF. Ale jeśli !
nie, i zamiast tego pomyślnie zapełni stos, wywołuje T
ponownie est.
d lm<M
- T
est następnie d
zastosuje górę stosu i porówna go $max
(tak jak w pamięci m
) . Jeśli m
jest to mniejsza wartość, dc
wywołuje M
makro.
s0 lm
- M
po prostu wyskakuje z góry stosu i zrzuca go do manekina skalarnego 0
- po prostu tani sposób na zerwanie stosu. To także ponownie l
owija m
się przed powrotem do T
est.
p
- Oznacza to, że jeśli m
jest mniejszy niż aktualny wierzchołek stosu, to m
zastępuje go (w d
każdym razie jego egzemplarz) i jest tutaj p
podszyty, w przeciwnym razie nie zostanie podany, a wszystko, co zostało wprowadzone, jest p
podszyte.
s0
- Następnie (ponieważ p
nie wyskakuje stos) ponownie wrzucamy górę stosu 0
, a następnie ...
lTx
- rekurencyjnie l
oad T
est jeszcze raz, a potem x
ponownie.
Abyś mógł uruchomić ten mały fragment kodu i interaktywnie wpisywać liczby w swoim terminalu i dc
wydrukować ci albo wpisany numer, albo wartość, $max
jeśli wpisany numer był większy. Akceptuje również dowolny plik (taki jak potok) jako standardowe wejście. Będzie kontynuował pętlę odczytu / porównania / drukowania, aż napotka pustą linię lub EOF.
Kilka uwag na ten temat - napisałem to tylko w celu naśladowania zachowania w twojej funkcji powłoki, więc solidnie obsługuje tylko jedną liczbę w wierszu. dc
może jednak obsłużyć tyle liczb oddzielonych spacjami w wierszu, ile chcesz w to rzucić. Jednak ze względu na stos, ostatnia liczba w linii kończy się jako pierwsza, na której działa, i tak, jak napisano, dc
wydrukowałby swoje wyniki w odwrotnej kolejności, jeśli wydrukowałbyś / wpisała więcej niż jedną liczbę w linii. obsłużyć to, aby zapisać linię w tablicy, a następnie ją przetworzyć.
Lubię to:
dc -e "${max}sm
[ d lm<M la 1+ d sa :a z0!=A ]SA
[ la d ;ap s0 1- d sa 0!=P ]SP
[ ? z 0=q lAx lPx l?x ]S?
[q]Sq [ s0 lm ]SM 0sa l?x"
Ale ... nie wiem, czy chcę to wyjaśnić tak samo głęboko. Wystarczy powiedzieć, że podczas dc
odczytu każdej wartości na stosie przechowuje albo swoją wartość, albo $max
wartość w indeksowanej tablicy, a gdy wykryje, że stos jest ponownie pusty, drukuje każdy indeksowany obiekt przed próbą odczytania innego linia wprowadzania.
I tak, podczas gdy pierwszy skrypt robi ...
10 15 20 25 30 ##my input line
20
20
20
15
10 ##see what I mean?
Drugi robi:
10 15 20 25 30 ##my input line
10 ##that's better
15
20
20 ##$max is 20 for both examples
20
Możesz obsługiwać zmiennoprzecinkowe o dowolnej dokładności, jeśli ustawisz je najpierw za pomocą k
polecenia. I możesz niezależnie zmieniać i
radia nput lub o
utput - co czasami może być przydatne z powodów, których możesz się nie spodziewać. Na przykład:
echo 100000o 10p|dc
00010
... która najpierw ustawia podstawową dc
wartość wyjściową na 100000, a następnie drukuje 10.
dc
czasu do czasu wypuszcza kilka liczb, aby utrzymać go na palcach.dc
jest kapryśną bestią, ale może być najszybszym i najdziwniej działającym wspólnym narzędziem w dowolnym systemie uniksowym. Po sparowaniu w /sed
może robić niezwykłe rzeczy. Bawiłem się nimdd
ostatnio, aby zastąpić potwornośćreadline
. Oto mała próbka niektórych rzeczy, które robiłem. Robienierev
indc
jest prawie dziecinnie proste.[string]P91P93P[string]P
. Mam więc trochę zsed
was, które mogą się przydać:sed 's/[][]/]P93]&[1P[/g;s/[]3][]][[][1[]//g'
które zawsze powinny poprawnie zamieniać kwadraty na nawias zamykający łańcuch, następnie aP
, następnie dziesiętną wartość ascii kwadratu i innąP
; następnie otwarty[
nawias kwadratowy, aby kontynuować ciąg. Nie wiem, czy masz problemy zdc
konwersją ciągów znaków / liczb, ale - szczególnie w połączeniu zod
- może być całkiem fajna.Jeśli wiesz, że mają do czynienia z dwóch liczb całkowitych
a
ab
, to te proste powłoki arytmetyczne rozszerzenia za pomocą operatora potrójny są wystarczające, aby dać maksymalną liczbowej:i numeryczne min:
Na przykład
Oto skrypt powłoki, który to pokazuje:
źródło
max=$(( a >= b ? a : b ))
, ale wynik jest całkowicie taki sam - jeśli aib są równe, to tak naprawdę nie ma znaczenia, który z nich zostanie zwrócony. Czy o to pytasz?if (( a >= b )); then echo a is greater than or equal to b; fi
- czy o to prosisz? (zwróć uwagę na użycie(( ))
tutaj zamiast$(( ))
)sort
ihead
może to zrobić:źródło
O(n log(n))
podczas gdy wydajna implementacja max byłabyO(n)
. Jest to jednak nasze małe znaczenien=2
, ponieważ pojawienie się dwóch procesów jest znacznie większe.numbers="1 4 3 5 7 1 10 21 8";
echo $numbers | tr ' ' "\n" | sort -rn | head -n 1
max=0; for x in $numbers ; do test $x -gt $max && max=$x ; done
Możesz zdefiniować bibliotekę predefiniowanych funkcji matematycznych,
bc
a następnie użyć ich w wierszu poleceń.Na przykład w pliku tekstowym, takim jak
~/MyExtensions.bc
:Teraz możesz zadzwonić
bc
:Do Twojej dyspozycji są bezpłatne funkcje biblioteki matematycznej, takie jak ta dostępna online.
Korzystając z tego pliku, możesz łatwo obliczyć bardziej skomplikowane funkcje, takie jak
GCD
:źródło
bc
to po prostudc
nakładki do dnia dzisiejszego, nawet jeśli GNUbc
już nie jest takie (ale GNUdc
i GNUbc
dzielą się ogromną ilością swojej bazy kodów) . W każdym razie może to być najlepsza odpowiedź tutaj.bc
również bezpośrednio przed wywołaniem funkcji. Nie potrzeba drugiego pliku :)Za długo na komentarz:
Chociaż możesz robić te rzeczy np. Za pomocą kombinacji
sort | head
lubsort | tail
, wydaje się to raczej nieoptymalne zarówno pod względem zasobów, jak i obsługi błędów. Jeśli chodzi o wykonanie, kombinacja oznacza odrodzenie 2 procesów tylko w celu sprawdzenia dwóch linii. To wydaje się trochę przesadzone.Poważniejszym problemem jest to, że w większości przypadków trzeba wiedzieć, że dane wejściowe są rozsądne, to znaczy zawierają tylko liczby. Rozwiązanie @ glennjackmann sprytnie rozwiązuje ten problem, ponieważ
printf %d
powinno obijać się liczbami innymi niż całkowite. To również nie będzie działać z liczbami zmiennoprzecinkowymi (chyba że zmienisz specyfikator formatu na%f
, gdzie wystąpią problemy z zaokrąglaniem).test $1 -gt $2
pokaże, czy porównanie się nie powiodło (status wyjścia 2 oznacza błąd podczas testu. Ponieważ zwykle jest to wbudowana powłoka, nie powstaje żaden dodatkowy proces - mówimy o kolejności setek razy szybsze wykonanie. Działa jednak tylko z liczbami całkowitymi.Jeśli musisz porównać kilka liczb zmiennoprzecinkowych, ciekawą opcją może być
bc
:byłoby równoważne
test $1 -gt $2
i użycie w powłoce:jest wciąż prawie 2,5 razy szybszy niż
printf | sort | head
(dla dwóch liczb).Jeśli możesz polegać na rozszerzeniach GNU w
bc
, możesz także użyćread()
funkcji do odczytania liczb bezpośrednio dobc
sript.źródło
dc -e "${max}sm[z0=?dlm<Mps0lTx]ST[?z0!=Tq]S?[s0lm]SMlTx"
- och, tyle żedc
robi to wszystko (z wyjątkiem echa, chociaż mogłoby) - czyta standardowe$max
wejście i wypisuje albo numer wejściowy, zależnie od tego jest mniejszy. W każdym razie tak naprawdę nie chcę tego wyjaśniać, a twoja odpowiedź jest lepsza, niż zamierzałam napisać. Więc proszę o moje głosowanie.dc
skrypt byłby naprawdę fajny, RPN nie jest obecnie tak często widywany.dc
może samodzielnie wykonać operacje we / wy, byłoby to jeszcze bardziej eleganckie niż.Aby uzyskać większą wartość $ a i $ b, użyj tego:
Ale potrzebujesz czegoś wokół tego, prawdopodobnie nie chcesz wykonać liczby, więc aby wyświetlić większą wartość dwóch, użyj „echo”
Powyższe ładnie pasuje do funkcji powłoki, np
Aby przypisać większą z dwóch do zmiennej, użyj tej zmodyfikowanej wersji:
lub użyj zdefiniowanej funkcji:
Odmiana funkcji daje również możliwość dokładnego dodania sprawdzania błędów wejściowych.
Aby zwrócić maksymalnie dwie liczby dziesiętne / zmiennoprzecinkowe, możesz użyć
awk
EDYCJA: Za pomocą tej techniki można utworzyć funkcję „limit”, która działa w drugą stronę, zgodnie z edycją / notatką. Ta funkcja zwróci dolną z dwóch, np .:
Lubię umieszczać funkcje narzędzia w osobnym pliku, wywoływać go
myprogram.funcs
i używać w skrypcie w następujący sposób:FWIW nadal robi to, co zrobiłeś, a twoja wersja, chociaż jest bardziej szczegółowa, jest równie wydajna.
Bardziej kompaktowa forma nie jest tak naprawdę lepsza, ale zapobiega bałaganowi w twoich skryptach. Jeśli masz wiele prostych konstrukcji if-then-else-fi, skrypt szybko się rozwija.
Jeśli chcesz ponownie użyć sprawdzania większych / mniejszych liczb wiele razy w jednym skrypcie, umieść je w funkcji. Format funkcji ułatwia debugowanie i ponowne użycie oraz pozwala łatwo zastąpić tę część skryptu, na przykład komendą awk, aby móc obsługiwać liczby dziesiętne niecałkowite.
Jeśli jest to pojedynczy przypadek użycia, po prostu koduj go w linii.
źródło
Możesz zdefiniować funkcję jako
Nazwij to jak
maxnum 54 42
i echo54
. Możesz dodać informacje o sprawdzeniu poprawności do funkcji (takie jak dwa argumenty lub liczby jako argumenty), jeśli chcesz.źródło
function maxnum {
namaxnum() {
i będzie działać na znacznie więcej pocisków.Ze skryptu powłoki istnieje sposób na użycie dowolnej publicznej metody statycznej Java (i na przykład Math.min () ). Z bash na Linuksie:
Wymaga to Java Shell Bridge https://sourceforge.net/projects/jsbridge/
Bardzo szybko, ponieważ wywołania metod są przetwarzane wewnętrznie ; nie wymaga procesu.
źródło
Większość ludzi po prostu by to zrobiła
sort -n input | head -n1
(lub ogon), co jest wystarczające w większości sytuacji skryptowych. Jest to jednak nieco niezdarne, jeśli masz liczby w wierszu zamiast w kolumnie - musisz wydrukować go w odpowiednim formacie (tr ' ' '\n'
lub coś podobnego).Powłoki nie są idealnie idealne do przetwarzania numerycznego, ale można łatwo po prostu podłączyć do innego programu, który jest w tym lepszy. W zależności od własnych preferencji możesz wywołać maksimum
dc
(trochę zaciemnione, ale jeśli wiesz, co robisz, jest w porządku - patrz odpowiedź mikeserv) lubawk 'NR==1{max=$1} {if($1>max){max=$1}} END { print max }'
. Lub ewentualnieperl
lubpython
jeśli wolisz. Jednym rozwiązaniem (jeśli chcesz zainstalować i używać mniej znanego oprogramowania) byłobyised
(szczególnie jeśli twoje dane są w jednym wierszu: wystarczy to zrobićised --l input.dat 'max$1'
).Ponieważ prosisz o dwie liczby, to wszystko jest przesada. To powinno wystarczyć:
źródło
sys.argv
:python2 -c 'import sys; print (max(sys.argv))' "$@"
sort + head
są przesadne, alepython
nie są obliczane.python
ponieważ jest schludne.python
bigotem (lub ponieważ nie wymaga on widelca i dodatkowego gigantycznego tłumacza) . A może jedno i drugie.