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.