A bináris számjegyek végső soron közvetlenül a processzorod vagy a memóriád tranzisztorainak felelnek meg: annak, hogy mindegyik „be” vagy „ki” állapotban van-e.
A mély szintű manipuláció, amit köznapi nevén „bit-twiddlingnek” hívnak, különösen fontos a rendszerprogramozási nyelvekben.
A Juliahoz hasonló magas szintű nyelvek azonban általában elfedik ezeknek a részleteknek a nagy részét. A nyelv alapjaiban viszont a bitszintű műveletek teljes skálája elérhető.
Megjegyzés: Ahhoz, hogy a REPL-ben ember által olvasható bináris kimenetet láss, az alábbi példák szinte mindegyikét be kell csomagolni egy bitstring() függvénybe.
Ez vizuálisan zavaró, ezért a függvény legtöbb előfordulását kivettem a példákból.
Az egész szám típusok, akár előjelesek, akár előjel nélküliek, egyesekből és nullákból álló stringként ábrázolhatók.
julia> bitstring(UInt8(5))
"00000101"
A biteltolás mindössze annyit tesz, hogy mindent balra vagy jobbra mozgat egy megadott számú pozícióval.
Az UInt típusoknál néhány bit leesik az egyik végén, a másik végét pedig nullákkal töltjük fel:
julia> ux::UInt8 = 5
5
julia> bitstring(ux)
"00000101"
julia> ux << 2 # left by 2
"00010100"
julia> ux >> 1 # right by 1
"00000010"
Minden balra eltolás megduplázza az értéket, minden jobbra eltolás pedig megfelezi (a csonkolástól függően). Ez tizedes ábrázolásban még szembetűnőbb:
julia> 3 << 2
12
julia> 24 >> 3
3
Ez a fajta biteltolás sokkal gyorsabb, mint a „rendes” aritmetika, ezért ez a technika nagyon népszerű a mély szintű programozásban.
Az előjeles egészeknél egy kicsit óvatosabbnak kell lennünk.
A balra eltolás viszonylag egyszerű:
julia> sx = Int8(5)
5
julia> sx # positive integer
"00000101"
julia> sx << 2
"00010100"
julia> -sx # negative integer
"11111011"
julia> -sx << 2
"11101100"
A pozitív előjeles egészek balra eltolása tehát ugyanaz, mint az előjel nélküli egészeké.
A negatív értékek kettes komplemens formában tárolódnak, ami azt jelenti, hogy a legbaloldalibb bit 1. Balra eltolásnál nincs gond, de jobbra eltolásnál mivel töltjük fel a legbaloldalibb biteket?
julia> sx >> 2 # simple for positive values!
"00000001"
julia> -sx # negative integer
"11111011"
julia> -sx >> 2 # pad with repeated sign bit
"11111110"
julia> -sx >>> 2 # pad with 0
"00111110"
A >> operátor aritmetikai eltolást végez, megőrizve az előjelbitet.
A >>> operátor logikai eltolást végez, nullákkal töltve fel, mintha a szám előjel nélküli lenne.
Egy korábbi fogalomnál láttuk, hogy a && (és), || (vagy) és ! (nem) operátorokat Boolean értékekkel használjuk.
Vannak ezzel egyenértékű operátorok, a & (bitenkénti és), a | (bitenkénti vagy) és a ~ (tilde, bitenkénti nem), amelyek két egész szám bitjeit hasonlítják össze.
julia> 0b1011 & 0b0010 # bit is 1 in both numbers
"00000010"
julia> 0b1011 | 0b0010 # bit is 1 in at least one number
"00001011"
julia> ~0b1011 # flip all bits
"11110100"
julia> xor(0b1011, 0b0010) # bit is 1 in exactly one number, not both
"00001001"
Itt az xor() a kizáró vagy, függvényként használva.
A barátod épp most küldött neked egy üzenetet egy fontos titokkal. Mivel nem akarta, hogy mások könnyen elolvashassák, az üzenetet bitműveletek sorozatával titkosította. Neked kell megírnod a függvényeket, amelyek segítenek visszafejteni az üzenetet.
Az üzenet visszafejtésének első lépése, hogy a titkosítás során eltolt biteket visszatoljuk jobbra. A visszafejtés további lépései abból indulnak ki, hogy a 0-k a bal oldalról kerülnek be.
Valósítsd meg a shift_back() függvényt, amely egy értéket és az eltolás helyeinek számát kapja, és végrehajtja az eltolást.
shift_back(0b1001, 2) # => 0b0010
Ezután néhány bitet 1-re kell állítani.
Valósítsd meg a set_bits() függvényt, amely egy értéket és egy maszkot kap, és visszaadja annak eredményét, hogy az érték bitjeit 1-re állítja.
Az value bitjét ott kell 1-re állítani, ahol a mask megfelelő bitje is 1.
Minden más bit változatlan marad.
set_bits(0b0110, 0b0101) # => 0b0111
A titkosítás során bizonyos bitek megfordulnak. Ezeket vissza kell fordítani az üzenet visszafejtéséhez.
Valósítsd meg a flip_bits() függvényt, amely egy értéket és egy maszkot kap.
A maszk megmondja, hogy az érték mely bitjeit kell megfordítani.
Ahol a mask bitje 1, ott a value megfelelő bitje megfordul.
Minden más bit változatlan marad.
flip_bits(0b1100, 0b0101) # => 0b1001
Végül vannak olyan bitek is, amelyek mindig 0-ra fejtődnek vissza.
Valósítsd meg a clear_bits() függvényt, amely egy értéket és egy maszkot kap.
A value bitjeit ott kell 0-ra állítani, ahol a maszk bitje 1.
Minden más bit változatlan marad.
clear_bits(0b0110, 0b0101) # => 0b0010
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Julia nyelvet 35 fogalom128 feladat segítségével, valódi emberi mentorálással, mindez ingyen.