Ders 16 / 17
Tipli Diziler
Sabit boyutlu ikili arabellekler ve üzerlerindeki tipli görünümler, taşma ile kırpma davranışı, bayt sırasının açıkça belirtilmesi ve tipli dizilerin sıradan dizilerden farkları.
İçindekiler
Bu konudaki koleksiyonların hepsi keyfi JavaScript değerleri tuttu. Bir ölçüm akışını dosyaya yazmak ya da ağ üzerinden aktarmak gerektiğinde ise veri, belirli sayıda bayta oturan sabit bir düzende temsil edilmelidir.
Tipli diziler (typed arrays) bu iş için vardır. İkisi bir arada çalışır: ham baytları
tutan ArrayBuffer ve o baytları belirli bir sayı türü olarak yorumlayan görünüm.
Bilgisayarlar Nasıl Çalışır kursundaki İşaretli Tam Sayılar, Kayan Noktalı Sayılar ve
Bayt Sırası derslerinde kurulan temsiller, burada doğrudan gözlemlenebilir hâle gelir.
Arabellek ve Görünüm
ArrayBuffer yalnızca bayt tutar; içeriğine doğrudan erişilmez. Erişim için üzerine bir
görünüm kurulur ve görünümün türü, aynı baytların nasıl okunacağını belirler.
const arabellek = new ArrayBuffer(16); console.log(arabellek.byteLength); const baytGorunumu = new Uint8Array(arabellek); const ondalikGorunum = new Float64Array(arabellek); console.log(baytGorunumu.length); console.log(ondalikGorunum.length); console.log(Uint8Array.BYTES_PER_ELEMENT); console.log(Float64Array.BYTES_PER_ELEMENT); ondalikGorunum[0] = 21.4; console.log(ondalikGorunum[0]); console.log([...baytGorunumu.slice(0, 8)].join(",")); baytGorunumu[0] = 0; console.log(ondalikGorunum[0]); console.log(baytGorunumu.buffer === ondalikGorunum.buffer); const bagimsiz = new Float64Array(2); console.log(bagimsiz.buffer === arabellek); console.log(bagimsiz[0]);
16 16 2 1 8 21.4 102,102,102,102,102,102,53,64 21.399999999999636 true false 0
Aynı on altı bayt, bir görünümde on altı tam sayı, diğerinde iki ondalık sayıdır. İki görünüm de aynı belleğe bakar: ondalık görünüme yazılan değerin baytları bayt görünümünde okunur, bayt görünümünde yapılan tek bir değişiklik ondalık değeri bozar. Sekizinci satırdaki sapma, en düşük anlamlı baytın sıfırlanmasının kayan noktalı gösterimdeki karşılığıdır.
Bayt dizilişi platformun yerel bayt sırasına bağlıdır; yukarıdaki çıktı küçük uçlu bir makinede üretilmiştir. Büyük uçlu bir makinede aynı program farklı bir dizilim gösterir. Taşınabilirliğin nasıl sağlanacağı bu dersin üçüncü bölümünün konusudur.
Bir arabellek verilmeden kurulan görünüm, kendi arabelleğini yaratır ve sıfırlarla başlar.
Taşma ve Kırpma
Tipli dizinin bir gözü, türünün izin verdiği aralığın dışında bir değer alamaz. Aralık dışı değerlerin ne olacağı türe göre değişir.
const sarma = new Uint8Array(4); const kirpma = new Uint8ClampedArray(4); const isaretli = new Int8Array(4); const denemeler = [300, -1, 255.9, 128]; denemeler.forEach((deger, sira) => { sarma[sira] = deger; kirpma[sira] = deger; isaretli[sira] = deger; }); console.log([...sarma].join(",")); console.log([...kirpma].join(",")); console.log([...isaretli].join(",")); const tamsayi = new Int32Array(1); tamsayi[0] = 21.9; console.log(tamsayi[0]); const ondalik = new Float32Array(1); ondalik[0] = 21.4; console.log(ondalik[0]); console.log(ondalik[0] === 21.4); const cifteDuyarlik = new Float64Array(1); cifteDuyarlik[0] = 21.4; console.log(cifteDuyarlik[0] === 21.4);
44,255,255,128 255,0,255,128 44,-1,-1,-128 21 21.399999618530273 false true
Uint8Array değeri 256’ya göre sarar: 300 değeri 44 olur, -1 değeri 255. Uint8ClampedArray
ise aralığa kırpar: 300 değeri 255’te, -1 değeri 0’da durur. Int8Array aynı baytları
işaretli yorumlar; 128 değeri -128 olarak okunur — ikiye tümleyen gösteriminin doğrudan
sonucudur.
Tam sayı görünümlerinde ondalık kısım atılır, yuvarlama yapılmaz: 21.9 değeri 21 olur. Kırpmalı türde ise en yakın tam sayıya yuvarlanır; 255.9 değeri 256 olur ve aralığa kırpılarak 255’te kalır.
Son üç satır duyarlık farkını gösterir. Float32Array sayıyı tek duyarlıkta saklar ve
geri okunan değer aslından farklıdır; Float64Array ise JavaScript sayılarının kendi
gösterimini kullandığı için değeri aynen korur. İkili kayıt biçimlerinde alan genişliği
seçimi bu ödünleşimi doğrudan belirler.
Bayt Sırasını Açıkça Belirtmek
Tipli dizi görünümleri yerel bayt sırasını kullanır; bu, verinin başka bir makinede
okunması gerektiğinde sorun çıkarır. DataView, her okuma ve yazmada bayt sırasının
açıkça belirtilmesini sağlar.
Ölçüm kayıtlarını on iki baytlık sabit bir düzene yerleştirelim: dört bayt sensör numarası, sekiz bayt ölçüm değeri.
const KAYIT_BOYU = 12; function kayitlariYaz(kayitlar, kucukUclu) { const arabellek = new ArrayBuffer(KAYIT_BOYU * kayitlar.length); const gorunum = new DataView(arabellek); kayitlar.forEach((kayit, sira) => { const ofset = sira * KAYIT_BOYU; gorunum.setUint32(ofset, kayit.sensorNo, kucukUclu); gorunum.setFloat64(ofset + 4, kayit.deger, kucukUclu); }); return arabellek; } function kayitlariOku(arabellek, kucukUclu) { const gorunum = new DataView(arabellek); const sonuc = []; for (let ofset = 0; ofset < arabellek.byteLength; ofset += KAYIT_BOYU) { sonuc.push({ sensorNo: gorunum.getUint32(ofset, kucukUclu), deger: gorunum.getFloat64(ofset + 4, kucukUclu), }); } return sonuc; } const kayitlar = [ { sensorNo: 1, deger: 21.4 }, { sensorNo: 2, deger: 19.8 }, ]; const kucukUcluArabellek = kayitlariYaz(kayitlar, true); const buyukUcluArabellek = kayitlariYaz(kayitlar, false); console.log(kucukUcluArabellek.byteLength); console.log([...new Uint8Array(kucukUcluArabellek).slice(0, 4)].join(",")); console.log([...new Uint8Array(buyukUcluArabellek).slice(0, 4)].join(",")); console.log(JSON.stringify(kayitlariOku(kucukUcluArabellek, true))); console.log(JSON.stringify(kayitlariOku(buyukUcluArabellek, false))); console.log(kayitlariOku(buyukUcluArabellek, true)[0].sensorNo);
24
1,0,0,0
0,0,0,1
[{"sensorNo":1,"deger":21.4},{"sensorNo":2,"deger":19.8}]
[{"sensorNo":1,"deger":21.4},{"sensorNo":2,"deger":19.8}]
16777216
İki arabellek aynı kayıtları taşır ama baytları ters sırada dizilidir. Bu çıktı platformdan
bağımsızdır: DataView yazımda ve okumada sırayı açıkça aldığı için makinenin yerel
sırasına bakmaz.
Son satır yanlış varsayımın bedelini gösterir. Büyük uçlu yazılmış veri küçük uçlu
okunduğunda 1 yerine 16777216 çıkar; hata yükselmez, yalnızca sayı anlamsızlaşır.
Bilgisayar Ağları müfredatındaki ağ bayt sırası tartışması aynı sorunun protokol
düzeyindeki karşılığıdır: ikili bir biçim tanımlanırken bayt sırası biçimin parçası olarak
yazılmalıdır.
Sıradan Diziden Farkları
Tipli diziler Array türünün bir alt türü değildir. Prototip zincirleri ayrıdır ve dizi
davranışlarının bir kısmını taşımazlar.
const tipli = new Float64Array([21.4, 19.8, 25.1]); const siradan = [21.4, 19.8, 25.1]; console.log(Array.isArray(tipli)); console.log(Array.isArray(siradan)); console.log(typeof tipli.push); console.log(typeof siradan.push); tipli[5] = 99; console.log(tipli.length); console.log(tipli[5]); const ustPrototip = Object.getPrototypeOf(Float64Array.prototype); console.log(ustPrototip === Object.getPrototypeOf(Uint8Array.prototype)); console.log(ustPrototip === Array.prototype); console.log(Object.getPrototypeOf(ustPrototip) === Object.prototype); const esleme = tipli.map((d) => d * 2); console.log(esleme.constructor === Float64Array); console.log([...esleme].join(",")); console.log([...tipli].join(",")); console.log(JSON.stringify(tipli)); console.log(JSON.stringify(Array.from(tipli)));
false
true
undefined
function
3
undefined
true
false
true
true
42.8,39.6,50.2
21.4,19.8,25.1
{"0":21.4,"1":19.8,"2":25.1}
[21.4,19.8,25.1]
Uzunluk sabittir: sınır dışına yazma sessizce yok sayılır, push gibi büyüten yöntemler
bulunmaz. Bu, Veri Yapıları kursundaki Diziler ile Dinamik Diziler ayrımının dildeki
karşılığıdır — tipli dizi birincisi, sıradan dizi ikincisidir.
Zincir yapısı da ayrıdır. Tüm tipli dizi türleri ortak bir ara prototipi paylaşır; map,
filter, reduce gibi yöntemler orada tanımlıdır ve Array.prototype ile hiçbir bağları
yoktur. Ara prototipin prototipi doğrudan Object.prototype nesnesidir. map çağrısının
yine tipli dizi döndürmesi de bu ayrı gerçekleştirimin sonucudur.
Dizileştirmede fark belirgindir: JSON.stringify tipli diziyi sayısal anahtarlı bir nesne
olarak yazar, dizi olarak değil. İkili veriyi metin biçiminde taşımak gerekiyorsa önce
sıradan diziye çevrilmelidir.
Görünüm mü, Kopya mı
Aynı arabellek üzerinde çalışan iki yöntem, bu kursun son dersine doğrudan bağlanan bir
ayrım kurar: subarray bir görünüm, slice bir kopya döndürür.
const tumOlcumler = new Float64Array([21.4, 19.8, 25.1, 18.2, 30.0]); const pencereGorunumu = tumOlcumler.subarray(1, 4); const pencereKopyasi = tumOlcumler.slice(1, 4); console.log([...pencereGorunumu].join(",")); console.log([...pencereKopyasi].join(",")); console.log(pencereGorunumu.buffer === tumOlcumler.buffer); console.log(pencereKopyasi.buffer === tumOlcumler.buffer); pencereGorunumu[0] = 0; pencereKopyasi[1] = 0; console.log([...tumOlcumler].join(",")); console.log([...pencereKopyasi].join(",")); console.log(pencereGorunumu.byteOffset); console.log(pencereKopyasi.byteOffset); console.log(tumOlcumler.byteLength);
19.8,25.1,18.2 19.8,25.1,18.2 true false 21.4,0,25.1,18.2,30 19.8,0,18.2 8 0 40
İki pencere başlangıçta aynı değerleri gösterir. Görünüme yazmak asıl diziyi değiştirir, kopyaya yazmak değiştirmez. Bellek açısından da farklıdırlar: görünüm yeni bayt ayırmaz ve asıl arabellek içinde bir ofsetten başlar; kopya kendi arabelleğini yaratır.
Büyük ölçüm bloklarında pencere işlemleri için görünüm kullanmak, veriyi çoğaltmadan çalışmayı sağlar. Buna karşılık görünüm asıl arabelleği erişilebilir tuttuğu için, küçük bir pencere büyük bir arabelleği bellekte yaşatabilir — Zayıf Koleksiyonlar dersindeki erişilebilirlik tartışmasının bir başka biçimi.
Özet
ArrayBufferham baytları tutar; erişim, baytları belirli bir sayı türü olarak yorumlayan görünümlerle yapılır.- Aynı arabellek üzerinde birden çok görünüm kurulabilir ve hepsi aynı belleği paylaşır.
- Tam sayı görünümleri aralık dışı değerleri sarar, kırpmalı tür ise aralığa kırpar; ondalık kısım tam sayı türlerinde atılır.
- Tipli dizi görünümleri yerel bayt sırasını kullanır; taşınabilir ikili biçimler için
DataViewile sıra açıkça belirtilir. - Tipli diziler
Arraytüründen ayrı bir zincire sahiptir: uzunlukları sabittir, büyütülemezler ve dizileştirmede nesne olarak yazılırlar. subarrayaynı belleğe bakan bir görünüm,slicebağımsız bir kopya döndürür.
Sonraki Adım
Bu derste kopya ile görünüm ayrımı bayt düzeyinde göründü; aynı soru nesneler için kursun
başından beri açıktı. Nesne yayma zinciri kaybetti, Object.assign erişimcileri düz
değere çevirdi, JSON.stringify yöntemleri ve Map içeriğini attı. Son ders bu ayrımı
toplu olarak ele alacak: bir nesneyi kopyalarken neyin taşındığını, neyin düştüğünü ve
derin kopyanın hangi yollarla — hangi kayıplarla — yapıldığını gösterecek.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.