dc (datorprogram)
Originalförfattare |
Lorinda Cherry , Robert Morris ( AT&T Bell Laboratories ) |
---|---|
Utvecklare | Olika öppen källkod och kommersiella utvecklare |
Skrivet i | B |
Operativ system | Unix , Unix-liknande , Plan 9 |
Plattform | Cross-plattform |
Typ | Kommando |
dc ( desk calculator ) är en plattformsoberoende polsk kalkylator som stöder aritmetik med godtycklig precision . Det är skrivet av Lorinda Cherry och Robert Morris på Bell Labs och är ett av de äldsta Unix- verktygen , som till och med föregick uppfinningen av programmeringsspråket C. Liksom andra verktyg i den årgången har den en kraftfull uppsättning funktioner men kortfattad syntax. Traditionellt bc- kalkylatorprogrammet (med infix notation ) implementerats ovanpå dc.
Den här artikeln ger några exempel i ett försök att ge en allmän smak av språket; för en komplett lista med kommandon och syntax bör man konsultera man-sidan för ens specifika implementering.
Historia
dc är det äldsta bevarade Unix -språkprogrammet. När dess hem Bell Labs fick en PDP-11 var dc – skriven i B – det första språket som kördes på den nya datorn, även innan en assembler. Ken Thompson har ansett att dc var det allra första programmet som skrevs på maskinen.
Grundläggande operationer
För att multiplicera fyra och fem i dc (observera att det mesta av blanksteg är valfritt):
$ cat << EOF > cal.txt 4 5 * p EOF $ dc cal.txt 20 $
Resultaten är också tillgängliga från kommandona:
$ echo "4 5 * p" | dc
eller
$ dc - 4 5*pq 20 $ dc 4 5 * p 20 q $ dc -e '4 5 * p'
Detta översätts till "skjuta fyra och fem på stapeln, sedan, med multiplikationsoperatorn, skjut två element från stapeln, multiplicera dem och skjut resultatet till stapeln." Sedan p
för att undersöka (skriva ut till skärmen) det översta elementet på stacken. Kommandot q
avslutar den anropade instansen av dc. Observera att siffror måste vara åtskilda från varandra även om vissa operatorer inte behöver vara det.
0
Den aritmetiska precisionen ändras med kommandot k
, som ställer in antalet bråksiffror (antalet siffror efter punkten ) som ska användas för aritmetiska operationer. Eftersom standardprecisionen är noll, ger denna sekvens av kommandon som ett resultat:
2 3 / sid
Genom att justera precisionen med k
kan ett godtyckligt antal decimaler framställas. Denna kommandosekvens matar ut .66666
.
5 k 2 3 / sid
För att utvärdera : ( v
beräknar kvadratroten från toppen av stapeln och _
används för att mata in ett negativt tal):
12 _3 4 ^ + 11 / v 22 - sid
För att byta de två översta elementen i stacken, använd kommandot r .
För att duplicera det översta elementet, använd kommandot d .
Ingång/utgång
För att läsa en rad från stdin , använd ?
kommando. Detta utvärderar raden som om det vore ett dc-kommando, och därför är det nödvändigt att den är syntaktisk korrekt och utgör ett potentiellt säkerhetsproblem eftersom !
dc-kommandot möjliggör exekvering av godtycklig kommando.
Som nämnts ovan, skriver p
toppen av stapeln med en ny rad efter. n
öppnar toppen av stapeln och skriver ut den utan en efterföljande nyrad. f
skriver ut hela stapeln med en post per rad.
dc stöder också godtyckliga in- och utgångsradiser . Kommandot i
öppnar toppen av stacken och använder det för inmatningsbasen. Sexkantssiffror måste anges med versaler för att undvika kollisioner med dc-kommandon och är begränsade till AF. Kommandot o
gör samma sak för utgångsbasen, men kom ihåg att ingångsbasen påverkar analysen av varje numeriskt värde efteråt, så det är vanligtvis lämpligt att ställa in utgångsbasen först. Därför 10o
utgångsradien till den aktuella ingångsradien, men i allmänhet inte till 10 (tio). Ändå Ao
utgångsbasen till 10 (tio), oavsett ingångsbas. För att läsa värdena K
, I
och O
på strömprecisionen, ingångsradix och utmatningsradix till toppen av stacken.
Som ett exempel, för att konvertera från hex till binär:
$ echo 16i2o DEADBEEFp | dc 11011110101011011011111011101111
Språkfunktioner
Register
Utöver dessa grundläggande aritmetiska och stackoperationer innehåller dc stöd för makron , villkor och lagring av resultat för senare hämtning.
Mekanismen bakom makron och villkor är registret , som i dc är en lagringsplats med ett enda teckennamn som kan lagras till och hämtas från: sc
öppnar toppen av stacken och lagrar den i register c, och lc
trycker på värdet av register c till stacken. Till exempel:
3 fm 4 lc * sid
Register kan också behandlas som sekundära stackar, så värden kan skjutas och skjutas mellan dem och huvudstacken med S-
och L
-kommandona.
Strängar
Strängvärden är inneslutna i [
och ]
tecken och kan skjutas in i stacken och lagras i register. Kommandot a
konverterar det numeriska värdets byte av låg ordning till ett ASCII- tecken, eller om toppen av stacken är en sträng ersätter det det med det första tecknet i strängen. Det finns inga sätt att bygga upp strängar eller utföra strängmanipulering annat än att köra det med x
, eller skriva ut det med P
-kommandot.
Tecknet #
börjar en kommentar till slutet av raden.
Makron
Makron implementeras sedan genom att låta register och stackposter vara såväl strängar som nummer. En sträng kan skrivas ut, men den kan också köras (dvs. bearbetas som en sekvens av dc-kommandon). Så till exempel kan vi lagra ett makro för att lägga till ett och sedan multiplicera med 2 i registret m:
[1 + 2 *] sm
och sedan (med x
-kommandot som kör toppen av stacken) kan vi använda det så här:
3 lm xp
Villkor
Slutligen kan vi använda denna makromekanism för att tillhandahålla villkor. Kommandot =r
poppar upp två värden från stacken och exekverar makrot lagrat i register r
endast om de är lika. Så detta skriver ut strängen lika
endast om de två översta värdena på stapeln är lika värdefulla:
[[lika]p] sm 5 5 =m
Andra villkor är >
, !> ,
< ,
! < ,
!=
, som exekverar det angivna makrot om de två översta värdena på stacken är större, mindre än eller lika med ("inte större"), mindre än, större än eller lika med ("inte mindre än"), respektive inte lika. Observera att ordningen på operanderna i olikhetsjämförelser är den motsatta till ordningen för aritmetik; 5 3 - utvärderas till 5 - 3 = 2
, men 5 3 <t kör innehållet i t - registret eftersom 3 < 5
.
Slingor
Looping är då möjligt genom att definiera ett makro som (villkorligt) återuppstår. En enkel factorial av toppen av stacken kan implementeras som:
# F(x): returnera x! # om x-1 > 1 # returnera x * F(x-1) # annars # returnera x [d1-d1
Kommandot 1Q
avslutas från ett makro, vilket möjliggör en tidig återgång. q
avslutas från två nivåer av makron (och dc själv om det finns mindre än två nivåer på anropsstacken). z
trycker på det aktuella stackdjupet före z
-operationen.
Exempel
Summering av hela stapeln
Detta implementeras med ett makro lagrat i register a
som villkorligt anropar sig självt och utför ett tillägg varje gång, tills endast ett värde finns kvar på stacken. z -
operatorn används för att skjuta upp antalet poster i stacken till stacken. Jämförelseoperatorn >
skjuter upp två värden från stacken när jämförelsen görs.
dc -e "1 2 4 8 16 100 0d[+2z>a]salaxp"
Och resultatet är 131.
Summera alla dc-uttryck som rader från filen
Ett blankt tal är ett giltigt dc-uttryck, så detta kan användas för att summera en fil där varje rad innehåller ett enda tal.
Detta implementeras återigen med ett makro lagrat i register a
som villkorligt anropar sig självt och utför en addition varje gång, tills endast ett värde återstår på stacken.
katt fil | dc -e "0d[?+2z>a]salaxp"
Den ?
operatören läser ett annat kommando från inmatningsströmmen. Om inmatningsraden innehåller ett decimaltal läggs det värdet till stacken. När indatafilen når slutet av filen är kommandot null och inget värde läggs till i stacken.
{ echo "5" ; eko "7" ; } | dc -e "0d[?+2z>a]salaxp"
Och resultatet är 12.
Ingångslinjerna kan också vara komplexa dc-kommandon.
{ echo "3 5 *" ; eko "4 3 *" ; echo "5dd++" ; } | dc -e "0d[?+2z>a]salaxp"
Och resultatet är 42.
Observera att eftersom dc stöder godtycklig precision, finns det ingen oro för numeriskt spill eller förlust av precision, oavsett hur många linjer ingångsströmmen innehåller, till skillnad från en liknande kortfattad lösning i AWK .
Nackdelar med denna lösning är: slingan stannar när den stöter på en tom rad i ingångsströmmen (tekniskt sett, vilken ingångsrad som helst som inte lägger till minst ett numeriskt värde till stacken); och, för att hantera negativa tal, måste inledande instanser av '-' för att beteckna ett negativt tecken ändras till '_' i ingångsströmmen, på grund av dc:s icke-standardiserade negativa tecken. Den ?
operatorn i dc ger inte ett rent sätt att urskilja att läsa en tom rad från att läsa slutet av filen.
Enhetskonvertering
Som ett exempel på ett relativt enkelt program i dc, detta kommando (på 1 rad):
dc -e '[[Ange ett tal (meter), eller 0 för att avsluta]psj]sh[q]sz[lhx?d0=z10k39.370079*.5+0k12~1/rn[ fot ]Pn[ inches]P10Pdx ]dx'
konverterar avstånd från meter till fot och tum; Det mesta handlar om att uppmana till inmatning, att skriva ut utdata i ett lämpligt format och att gå runt för att konvertera ett annat nummer.
Största gemensamma delare
Som ett exempel, här är en implementering av den euklidiska algoritmen för att hitta GCD :
dc -e '??[dSarLa%d0<a]dsax+p' # kortaste dc -e '[a=]P?[b=]P?[dSarLa%d0<a]dsax+[GCD:]Pp' # lättare att läsa version
Faktoriell
Beräknar faktorn för ett ingångsvärde,
dc -e '?[q]sQ[d1=Qd1-lFx*]dsFxp'
Quines i dc
Det finns även quines i programmeringsspråket dc; program som producerar dess källkod som utdata.
dc -e '[91Pn[dx]93Pn]dx' dc -e '[91PP93P[dx]P]dx'
Skriver ut alla primtal
echo '2p3p[dl!d2+s!%0=@l!l^!<#]s#[s/0ds^]s@[p]s&[ddvs^3s!l#x0<&2+lx]ds .x' | dc
Detta program skrevs av Michel Charpentier. Den matar ut sekvensen av primtal. Observera att en kortare implementering är möjlig, som läser fjorton symboler färre.
echo '2p3p[pq]s$[l!2+ds!l^<$dl!%0<#]s#[+dvs^1s!l#x2l.x]ds.x' | dc
Heltalsfaktorisering
dc -e '[n=]P?[p]s2[lip/dli%0=1dvsr]s12sid2%0=13sidvsr[dli%0=1lrli2+dsi!>.]ds.xd1<2'
Detta program skrevs också av Michel Charpentier.
Det finns en kortare
dc -e "[n=]P?[lfp/dlf%0=Fdvsr]sF[dsf]sJdvsr2sf[dlf%0=Flfdd2%+1+sflr<Jd1<M]dsMx"
och en snabbare lösning (försök med 200-bitars nummer 2 200 -1 (ingång 2 200^1-
)
dc -e "[n=]P?[lfp/dlf% 0=Fdvsr]sFdvsr2sfd2%0=F3sfd3%0=F5sf[dlf%0=Flfd4+sflr>M]sN[dlf%0=Flfd2+sflr>N ]dsMx[p]sMd1<M"
Observera att det senare kan påskyndas ännu mer, om tillgången till en konstant ersätts av en registeråtkomst.
dc -e "[n=]P?[lfp/dlf%l0=Fdvsr]sF2s2dvsr2sf4s4d2%0=F3sfd3%0=F5sf[dlf%l0=Flfdl4+sflr>M]sN[dlf%l0=Flfdl2+sflr ]dsMx[p]sMd1<M"
Beräknar Pi
En implementering av Chudnovsky-algoritmen i programmeringsspråket dc. Programmet kommer att skriva ut bättre och bättre uppskattningar när det körs. Men eftersom pi är ett transcendentalt tal, kommer programmet att fortsätta tills det avbryts eller resursutmattning av maskinen det körs på.
dc -e '_640320[0ksslk3^16lkd12+sk*-lm*lhd1+sh3^/smlxlj*sxll545140134+dsllm*lxlnk/ls+dls!=P]sP3^sj7sn[6sk1ddshs914209ds914209ds514200dsxsm14209ds514203d /K3-k1/pcln14+snlMx ]dsMx'
En snabb dela och erövra implementering av samma formel som fördubblas i storlek för varje iteration. Den utvärderar ett ändligt tal om summor som ett exakt rationellt tal och utför bara en stor division och kvadratrot per iteration. Det är snabbt, men kommer ändå snabbt att sakta ner när storleken på fraktionen ökar.
dc -e '1Sk1SR13591409dSBSP426880dSQ4/3^9*SC[0r-]s-[lkE*1-k10005vlQ*lP/nAan0k]dSox[Lkd1+Skdd1+Sk3^lC*SQ2*451*SR*1-4Bd +dSB*lk2%0=-SP]dszx[LRLRdLP*LPLQdLQ*SQ*+SP*SR]sc[d1-d0<yd0<yd0=z0=zlcx]sy0[lcxlox1+lyxllx]dslx'
Diffie–Hellman nyckelbyte
Ett mer komplext exempel på dc-användning inbäddad i ett Perl- skript utför ett Diffie–Hellman-nyckelutbyte . Detta var populärt som ett signaturblock bland cypherpunks under ITAR -debatterna, där det korta skriptet kunde köras med endast Perl och dc, allestädes närvarande program på Unix-liknande operativsystem:
#!/usr/bin/perl -- -export-a-crypto-system-sig Diffie-Hellman-2-lines ( $ g , $e , $m ) = @ARGV , $m || die "$0 gen exp mod\n" ; print `echo "16dio1[d2%Sa2/d0<X+d*La1=z\U$m%0]SX$e"[$g*]\EszlXx+p | dc`
En kommenterad version är något lättare att förstå och visar hur man använder loopar, villkor och kommandot q
för att återvända från ett makro. Med GNU-versionen av dc, |
kommando kan användas för att göra godtycklig precisionsmodulär exponentiering utan att behöva skriva X-funktionen.
#!/usr/bin/perl my ( $g , $e , $m ) = map { "\U$_" } @ARGV ; dö "$0 gen exp mod\n" om inte $m ; print `echo $g $e $m | dc -e ' # Hexingång och utgång 16dio # Läs m, e och g från stdin på en rad ?SmSeSg # Funktion z: return g * toppen av stack [lg*]sz # Funktion Q: ta bort toppen av stacken och return 1 [sb1q]sQ # Funktion X(e): beräkna rekursivt g^e % m # Det är samma sak som Sm^Lm%, men hanterar godtyckligt stora exponenter. # Stapel vid ingång: e # Stapel vid utfart: g^e % m # Eftersom e kan vara mycket stor, använder detta egenskapen att g^e % m == # if( e == 0 ) # return 1 # x = (g^(e/2)) ^ 2 # if( e % 2 == 1 ) # x *= g # retur x % [ d 0=Q # return 1 if e==0 (annars stack: e) d 2% Sa # Lagra e%2 i en (stack: e) 2/ # beräkna e/2 lXx # ring X(e/2) d* # beräkna X(e/2)^2 La1=z # multiplicera med g om e%2==1 lm % # beräkna (g^e) % m ] SX le # Ladda e från registret lXx # beräkna g^e % m p # Skriv ut resultatet '` ;
Miljövariabler
Om miljövariabeln DC_LINE_LENGTH finns och innehåller ett heltal som är större än 1 och mindre än kommer utmatningen av talsiffror (enligt utdatabasen) att begränsas till detta värde, sedan infogar snedstreck och nyrader. Standardradlängden är 70. Specialvärdet 0 inaktiverar radbrytningar.
Se även
externa länkar
- Paket dc i Debian GNU/Linux- förråd
- Plan 9 Programmerarmanual, volym 1 –
- Inbyggd Windows-port av bc , som inkluderar dc.
- dc inbäddad i en webbsida