dc (számítógépes program) - dc (computer program)

dc
Eredeti szerző (k) Robert Morris
( AT&T Bell Laboratories )
Fejlesztő (k) Különböző nyílt forráskódú és kereskedelmi fejlesztők
Beírták B
Operációs rendszer Unix , Unix-szerű , 9. terv
Felület Többplatformos
típus Parancs

A dc ( asztali számológép ) egy többplatformos, fordított polírozású számológép, amely tetszőleges pontosságú aritmetikát támogat . Robert Morris írta a Bell Labs -ban , ez az egyik legrégebbi Unix segédprogram, amely megelőzi még a C programozási nyelv feltalálását is . Az évjárat többi segédprogramjához hasonlóan erőteljes funkciókkal rendelkezik, de tömör szintaxissal. Hagyományosan a bc számológép programot ( infix jelöléssel ) a dc tetején hajtották végre.

Ez a cikk néhány példát kínál a nyelv általános ízének megadására; A parancsok és szintaxisok teljes listájáért keresse meg a kézikönyv oldalt a konkrét megvalósítás érdekében.

Történelem

dc a legrégebbi fennmaradt Unix nyelvi program. Amikor az otthoni Bell Labs megkapta a PDP-11-et , a dc- B nyelven írva- volt az első nyelv, amely az új számítógépen futott, még egy összeszerelő előtt. Ken Thompson úgy vélekedett, hogy a dc volt a legelső program, amelyet a gépre írtak.

Alapműveletek

Négy és öt szorzása egyenáramban (vegye figyelembe, hogy a szóközök nagy része nem kötelező):

$ cat << EOF > cal.txt
4 5 *
p
EOF

$ dc cal.txt
20
$

Az eredményt a következő parancsokkal is elérheti:

$ echo "4 5 * p" | dc

vagy

$ dc -
4 5*pq
20

$ dc
4 5 *
p
20
q

$ dc -e '4 5 * p'

Ez azt jelenti, hogy "nyomja a négyet és ötöt a veremre, majd a szorzó operátorral húzzon ki két elemet a veremből, szorozza meg őket, és nyomja vissza az eredményt a veremre." Ezután a pparanccsal megvizsgáljuk (kinyomtatjuk a képernyőre) a verem felső elemét. A qparancs kilép a dc meghívott példányából. Ne feledje, hogy a számokat egymástól távol kell elhelyezni, még akkor is, ha egyes operátoroknak nem szükséges.

Az aritmetikai pontosságot a parancs változtatja meg k, amely beállítja a számjegyek számát (a pontot követő számjegyek számát ), amelyet az aritmetikai műveletekhez használni kell. Mivel az alapértelmezett pontosság nulla, a parancsok sorozata 0ennek eredményeként jön létre:

2 3 / p

A pontosság beállításával ktetszőleges számú tizedesjegy állítható elő. Ez a parancssor kimenete .66666.

5 k
2 3 / p

Értékelés : ( kiszámítja a verem tetejének négyzetgyökét, és negatív szám bevitelére szolgál): v_

12 _3 4 ^ + 11 / v 22 -
p

A verem felső két elemének felcseréléséhez használja a rparancsot. A felső elem másolásához használja a dparancsot.

Bemenet kimenet

Ha egy sort szeretne olvasni az stdin -ből , használja a ?parancsot. Ez úgy értékeli a sort, mintha dc parancs lenne, ezért szükséges, hogy szintaktikailag helyes legyen, és potenciálisan biztonsági probléma legyen, mivel a !dc parancs lehetővé teszi a parancs tetszőleges végrehajtását.

Amint fentebb említettük, pa köteg tetejét új sorral nyomtatja utána. nfelugrik a verem tetejére, és azt egy új sor nélkül adja ki. faz egész köteget soronként egy bejegyzéssel dobja ki.

A dc tetszőleges bemeneti és kimeneti rádiókat is támogat . A iparancs felugrik a verem tetejére, és a bemeneti bázishoz használja. A hexadecimális számjegyeknek nagybetűnek kell lenniük, hogy elkerüljék a dc parancsokkal való ütközést, és az AF -re korlátozódnak. A oparancs ugyanezt teszi a kimeneti bázis esetében is, de ne feledje, hogy a bemeneti bázis utólag minden számérték értelmezésére hatással lesz, ezért általában ajánlatos először a kimeneti bázist beállítani. Ezért 10oa kimeneti radixot az aktuális bemeneti sugárra állítja, de általában nem 10 (tíz) értékre. Ennek ellenére Aovisszaállítja a kimeneti bázist 10 (tíz) értékre, függetlenül a bemeneti bázistól. Az értékek leolvasásához a K, Iés a Oparancsok a verem tetejére tolják az aktuális pontosságot, bemeneti és kimeneti radixot.

Példa: hatszögből binárisra konvertálás:

$ echo 16i2o DEADBEEFp | dc
11011110101011011011111011101111

Nyelvi jellemzők

Regiszterek

Ezen alapvető számtani és verem műveleteken kívül a dc tartalmazza a makrók , a feltételes feltételek támogatását és az eredmények tárolását a későbbi visszakereséshez.

A makrók és a feltételek alapjául szolgáló mechanizmus a regiszter , amely egyenáramban egy tárolóhely, egyetlen karakternévvel, amely tárolható és lekérhető: scfelugrik a verem tetejére, és tárolja a c regiszterben, és lcmegnyomja a regiszter értékét c a veremre. Például:

3 sc 4 lc * p

A regiszterek másodlagos veremként is kezelhetők, így az értékek a Sés a Lparancsok segítségével tolhatók és pattoghatnak közöttük és a fő verem között .

Húrok

A karakterláncok értékei [és ]karakterek közé vannak zárva, és a veremre tolhatók, és a regiszterekben tárolhatók. A aparancs a numerikus érték alacsony rendű bájtját ASCII karakterré alakítja , vagy ha a verem teteje karakterlánc, akkor lecseréli a karakterlánc első karakterére. Nincs más módszer a karakterláncok felépítésére vagy a karakterláncok manipulálására, mint a xparancs végrehajtása vagy a paranccsal történő nyomtatás P.

A #karakter megjegyzést kezd a sor végéig.

Makrók

A makrókat ezután úgy valósítják meg, hogy a regiszterek és verembejegyzések karakterláncok és számok is lehetnek. Egy karakterlánc nyomtatható, de végrehajtható is (azaz egyenáramú parancsok sorozataként feldolgozható). Például tárolhatunk egy makrót, hogy hozzáadjunk egyet, majd megszorozzuk 2 -vel az m regiszterbe:

[1 + 2 *] sm

majd ( xa verem tetejét végrehajtó paranccsal) a következőképpen használhatjuk:

3 lm x p

Feltételes

Végezetül használhatjuk ezt a makrómechanizmust feltételek biztosítására. A parancs =rkét értéket jelenít meg a veremből, és rcsak akkor hajtja végre a regiszterben tárolt makrót, ha azok egyenlők. Tehát ez equalcsak akkor nyomtatja ki a karakterláncot , ha a verem teteje 5 -tel egyenlő:

[[equal]p] sm 5 =m

Kondicionálisokhoz vannak >, !>, <, !<, !=, amely végrehajtja a megadott makrót, ha az első két érték a veremben nagyobb, kisebb vagy egyenlő ( „nem nagyobb”), kevesebb, mint, nagyobb vagy egyenlő ( "nem kevesebb, mint "), és nem egyenlő. Vegye figyelembe, hogy az operandusok sorrendje az egyenlőtlenségek összehasonlításában ellentétes a számtani sorrenddel; 5 3 - kiértékeli az 5 - 3 = 2 értéket, de 5 3 <t futtatja a t regiszter tartalmát, mert 3 <5 .

Hurok

A hurok ekkor lehetséges egy makró meghatározásával, amely (feltételesen) újraindítja önmagát. A verem tetejének egyszerű tényezőit a következőképpen lehet megvalósítani:

# F(x): return x!
# if x-1 > 1
#     return x * F(x-1)
# otherwise
#     return x
[d1-d1<F*]dsFxp

A 1Qparancs kilép a makróból, lehetővé téve a korai visszatérést. qkilép a makrók két szintjéről (és magából a dc -ből, ha két szintnél kevesebb van a híváskötegen). znyomja meg az aktuális kötegmélységet a zművelet előtt .

Példák

A teljes verem összegzése

Ez egy regiszterben tárolt makróval valósul meg, aamely feltételesen meghívja magát, és minden alkalommal kiegészítést hajt végre, amíg csak egy érték marad a veremben. A zkezelő a veremben lévő bejegyzések számát a veremre tolja. Az összehasonlító operátor >két értéket dob ​​ki a veremből az összehasonlítás során.

dc -e "1 2 4 8 16 100 0d[+2z>a]salaxp"

És az eredmény 131.

Az összes egyenáramú kifejezés összegzése a fájl soraiként

A csupasz szám érvényes egyenáramú kifejezés, így ezzel össze lehet foglalni egy fájlt, ahol minden sor egyetlen számot tartalmaz.

Ez ismét egy regiszterben tárolt makróval valósul meg, aamely feltételesen meghívja magát, és minden alkalommal kiegészítést hajt végre, amíg csak egy érték marad a veremben.

cat file | dc -e "0d[?+2z>a]salaxp"

A ?kezelő egy másik parancsot olvas ki a bemeneti adatfolyamból. Ha a beviteli sor tizedes számot tartalmaz, akkor az érték hozzáadódik a veremhez. Amikor a bemeneti fájl eléri a fájl végét, a parancs null, és a verem nem kerül hozzáadásra.

{ echo "5"; echo "7"; } | dc -e "0d[?+2z>a]salaxp"

És az eredmény 12.

A bemeneti sorok lehetnek összetett egyenáramú parancsok is.

{ echo "3 5 *"; echo "4 3 *"; echo "5dd++"; } | dc -e "0d[?+2z>a]salaxp"

És az eredmény 42.

Ne feledje, hogy mivel az egyenáramú rendszer tetszőleges pontosságot támogat, nem kell aggódnia a numerikus túlcsordulás vagy a pontosság elvesztése miatt, függetlenül attól, hogy a bemeneti adatfolyam hány sort tartalmaz, ellentétben az AWK hasonlóan tömör megoldásával .

Ennek a megoldásnak a hátrányai: a ciklus megszűnik, ha üres sort talál a bemeneti folyamban (technikailag minden olyan bemeneti sor, amely nem ad hozzá legalább egy számértéket a veremhez); és a negatív számok kezeléséhez a negatív előjelet jelölő „-” példányokat „_” -ra kell változtatni a bemeneti adatfolyamban, a dc nem szabványos negatív előjele miatt. Az ?egyenáramú operátor nem biztosít tiszta módot arra, hogy megkülönböztesse az üres sort a fájl végéről.

Egységátalakítás

Példa egy viszonylag egyszerű programra a dc -ben, ez a parancs (1 sorban):

dc -e '[[Enter a number (metres), or 0 to exit]psj]sh[q]sz[lhx?d0=z10k39.370079*.5+0k12~1/rn[ feet ]Pn[ inches]P10Pdx]dx'

átalakítja a távolságokat méterről lábra és hüvelykre; nagy része azzal foglalkozik, hogy kéri a bemenetet, kinyomtatja a megfelelő formátumot, és körbevág egy másik szám konvertálásához.

Legnagyobb közös osztó

Példaként itt van az euklideszi algoritmus megvalósítása a GCD megkereséséhez :

dc -e '??[dSarLa%d0<a]dsax+p'                  # shortest
dc -e '[a=]P?[b=]P?[dSarLa%d0<a]dsax+[GCD:]Pp' # easier-to-read version

Faktoriális

Számítástechnika a faktoriális a bemeneti érték,

dc -e '?[q]sQ[d1=Qd1-lFx*]dsFxp'

Quines dc -ben

A dc programozási nyelvben quinek is léteznek ; programok, amelyek kimenetként a forráskódját állítják elő.

dc -e '[91Pn[dx]93Pn]dx'

dc -e '[91PP93P[dx]P]dx'

Az összes prímszám nyomtatása

echo '2p3p[dl!d2+s!%0=@l!l^!<#]s#[s/0ds^]s@[p]s&[ddvs^3s!l#x0<&2+l.x]ds.x' | dc

Ezt a programot Michel Charpentier írta. A prímszámok sorozatát adja ki. Vegye figyelembe, hogy egy szimbólummal lerövidíthető, ami a minimális megoldásnak tűnik.

echo '2p3p[dl!d2+s!%0=@l!l^!<#]s#[0*ds^]s@[p]s&[ddvs^3s!l#x0<&2+l.x]ds.x' | dc

Egész faktorizálás

dc -e '[n=]P?[p]s2[lip/dli%0=1dvsr]s12sid2%0=13sidvsr[dli%0=1lrli2+dsi!>.]ds.xd1<2'

Ezt a programot szintén Michel Charpentier írta.

Van egy rövidebb

dc -e "[n=]P?[lfp/dlf%0=Fdvsr]sF[dsf]sJdvsr2sf[dlf%0=Flfdd2%+1+sflr<Jd1<M]dsMx"

és gyorsabb megoldás (próbálja meg a 200 bites számot 2 200 -1 (bemenet 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"

Ne feledje, hogy ez utóbbit még gyorsítani lehet, ha az állandóhoz való hozzáférést regiszter -hozzáféréssel helyettesítik.

dc -e "[n=]P?[lfp/dlf%l0=Fdvsr]sF2s2dvsr2sf4s4d2%0=F3sfd3%0=F5sf[dlf%l0=Flfdl4+sflr>M]sN[dlf%l0=Flfdl2+sflr>N]dsMx[p]sMd1<M"

Diffie – Hellman kulcscsere

A Perl szkriptbe ágyazott egyenáramú használat összetettebb példája Diffie – Hellman kulcscserét hajt végre . Ez népszerű aláírásblokk volt a cypherpunkok körében az ITAR viták során , ahol a rövid szkript csak Perl és dc, mindenütt jelen lévő programokkal futtatható Unix-szerű operációs rendszereken:

#!/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`

A megjegyzésekkel ellátott verzió valamivel könnyebben érthető, és bemutatja, hogyan kell használni a ciklusokat, a feltételes feltételeket és a qparancsot a makróból való visszatéréshez. A dc GNU verziójával a |paranccsal tetszőleges pontosságú moduláris hatványozást lehet végrehajtani anélkül, hogy meg kellene írni az X függvényt.

#!/usr/bin/perl

my ($g, $e, $m) = map { "\U$_" } @ARGV;
die "$0 gen exp mod\n" unless $m;

print `echo $g $e $m | dc -e '
# Hex input and output
16dio
# Read m, e and g from stdin on one line
?SmSeSg

# Function z: return g * top of stack
[lg*]sz

# Function Q: remove the top of the stack and return 1
[sb1q]sQ

# Function X(e): recursively compute g^e % m
# It is the same as Sm^Lm%, but handles arbitrarily large exponents.
# Stack at entry: e
# Stack at exit: g^e % m
# Since e may be very large, this uses the property that g^e % m == 
#     if( e == 0 )
#         return 1
#     x = (g^(e/2)) ^ 2
#     if( e % 2 == 1 )
#         x *= g
#     return x %
[
    d 0=Q   # return 1 if e==0 (otherwise, stack: e)
    d 2% Sa # Store e%2 in a (stack: e)
    2/      # compute e/2
    lXx     # call X(e/2)
    d*      # compute X(e/2)^2
    La1=z   # multiply by g if e%2==1
    lm %    # compute (g^e) % m
] SX

le          # Load e from the register
lXx         # compute g^e % m
p           # Print the result
'`;

Lásd még

Hivatkozások

Külső linkek