İçeriğe geç
academia.sh

Ders 08 / 14

Çok Boyutlu Diziler

Dizilerin dizilerle iç içe kurulması; eksik bir iç anahtarın E_WARNING ile null vermesi; null üzerinde dizi erişimi; boşluk birleştirme işlecinin yolun tamamını sessize çevirmesi; okuma ile yazmanın ayrı kanal olması.

Yayın Güncelleme

Bu derste

  • Dizi elemanı olarak dizi kullanarak iç içe veri modelini kurar ve her düzeyde anahtarla erişir
  • Eksik iç anahtarın E_WARNING ve null ürettiğini ölçer ve uyarı sınıfına yazar
  • Boşluk birleştirme işlecinin yolun tamamını sessize çevirdiğini ve okuma ile yazmanın ayrı kanal olduğunu gösterir
İçindekiler

İndeksli ve İlişkisel Diziler dersi, aynı anahtarın farklı yazımlarının tek elemana inmesini ve bunun hiçbir kanaldan duyulmadığını ölçtü. Anahtar tek bir değerdi. Bir elemanın kendisi başka bir dizi olduğunda erişim bir yola dönüşür: $a[0]['soyad'] iki ayrı arama yapar ve ikincisinin dayanağı birincinin sonucudur. Bu ders yolun bir halkası eksik olduğunda bunun nereden duyulduğunu ölçüyor.

Çok boyutlu dizi (multidimensional array) ayrı bir tip değildir; elemanları dizi olan sıradan bir dizidir. Derinlik için dilde bir sınır konmaz, yalnız bellek sınırlar. Ders sınıf adlarını önceki derslerdeki anlamıyla kullanır: sessiz, dönüş, uyarı, durdurur.

İç İçe Veri Modelleme

Bir öğrenci listesi, her öğrencinin adı ve not tablosuyla birlikte iç içe bir yapıya oturur. Aşağıdaki dosya bu yapıyı kuruyor, iki düzeyden okuyor ve count ile düz sayımı özyinelemeli sayımla karşılaştırıyor.

<?php
// ogrenciler.php — dizi icinde dizi ile veri modelleme
$ogrenciler = [
    ['ad' => 'Ada', 'notlar' => ['mat' => 80, 'fen' => 90]],
    ['ad' => 'Can', 'notlar' => ['mat' => 70, 'fen' => 60]],
];

foreach ($ogrenciler as $sira => $ogrenci) {
    $ortalama = array_sum($ogrenci['notlar']) / count($ogrenci['notlar']);
    echo $sira, ' ', $ogrenci['ad'], ' ', $ortalama, "\n";
}

echo 'Can fen: ', $ogrenciler[1]['notlar']['fen'], "\n";
echo 'duz sayim: ', count($ogrenciler), "\n";
echo 'ozyinelemeli sayim: ', count($ogrenciler, COUNT_RECURSIVE), "\n";
0 Ada 85
1 Can 65
Can fen: 60
duz sayim: 2
ozyinelemeli sayim: 10

Her düzey kendi anahtar uzayına sahiptir. Dış dizi indeksli, öğrenci kaydı ilişkisel, not tablosu yine ilişkisel; $ogrenciler[1]['notlar']['fen'] üç anahtarın sırayla uygulanmasıdır. count yalnız dış düzeyi sayar, COUNT_RECURSIVE bayrağı her iç diziyi hem bir eleman olarak hem içeriğiyle sayar; bu yüzden iki sayı arasındaki fark yapının derinliğini yansıtır, veri sayısını değil.

DZ6. Bu derste kayıtlar sabit biçimlidir: her öğrencide ad ve notlar anahtarı vardır. Yolun bozulduğu durumlar bir sonraki bölümde ayrıca kurulur. Bir kaydın biçiminin kullanıcıdan ya da dış bir kaynaktan geldiği durumlar M16/K01’in konusudur ve burada ele alınmaz.

Eksik İç Anahtar

Yol bir halkada kopunca ne olduğu, dinleyicinin kanal sırasıyla ölçülür. Dersin ortak yardımcısı kanal.php aşağıda tam metniyle veriliyor; çıktı bloğu olmadığı için yalnız diske yazılır.

<?php
// kanal.php — bir sorunun hangi kanaldan duyuldugunu siniflandiran yardimcilar

// Yakalanan uyarilarin seviye adlari burada birikir; ileti metni tutulmaz.
$GLOBALS['duyulan_uyarilar'] = [];

set_error_handler(function (int $seviye): bool {
    $adlar = [E_WARNING => 'E_WARNING', E_NOTICE => 'E_NOTICE', E_DEPRECATED => 'E_DEPRECATED'];
    $GLOBALS['duyulan_uyarilar'][] = $adlar[$seviye] ?? 'E_' . $seviye;
    return true;
});

// Degeri tipiyle birlikte kisa bir metne cevirir.
function yaz(mixed $deger): string
{
    return match (true) {
        $deger === null => 'null',
        is_bool($deger) => $deger ? 'true' : 'false',
        is_array($deger) => 'dizi(' . count($deger) . ')',
        default => get_debug_type($deger) . ' ' . $deger,
    };
}

// Isi calistirir ve sorunun hangi kanaldan duyuldugunu sirayla sorar:
// once firlatma, sonra uyari, sonra false/null donus; hicbiri yoksa sessiz.
function dinle(callable $is): array
{
    $GLOBALS['duyulan_uyarilar'] = [];
    try {
        $sonuc = $is();
    } catch (Throwable $hata) {
        return ['durdurur', get_class($hata)];
    }
    if ($GLOBALS['duyulan_uyarilar']) {
        $seviyeler = implode(',', array_unique($GLOBALS['duyulan_uyarilar']));
        return ['uyari', $seviyeler . ', sonuc ' . yaz($sonuc)];
    }
    if ($sonuc === false || $sonuc === null) {
        return ['donus', 'sonuc ' . yaz($sonuc)];
    }
    return ['sessiz', 'sonuc ' . yaz($sonuc)];
}

// Kodu ayri bir PHP yurutmesinde calistirir; cikis kodu 255 ise yurutme durmustur.
function ayri_yurut(string $kod): array
{
    exec('php -d display_errors=0 -r ' . escapeshellarg($kod) . ' 2>&1', $satirlar, $cikis);
    $sinif = $cikis === 255 ? 'durdurur' : 'sessiz';
    return [$sinif, 'cikti ' . count($satirlar) . ' satir, cikis ' . $cikis];
}
<?php
// eksik.php — eksik ic anahtar ve null uzerinde dizi erisimi
require 'kanal.php';

$kayitlar = [['ad' => 'Ada']];

$gozlemler = [
    'eksik ic anahtar'   => fn() => $kayitlar[0]['soyad'],
    'eksik dis anahtar'  => fn() => $kayitlar[5]['ad'],
    'null uzerinde erisim' => fn() => $kayitlar[5]['ad']['x'],
    'dizgi uzerinde erisim' => fn() => $kayitlar[0]['ad']['x'],
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-23s%-9s%s\n", $ad, $sinif, $kanit);
}
eksik ic anahtar       uyari    E_WARNING, sonuc null
eksik dis anahtar      uyari    E_WARNING, sonuc null
null uzerinde erisim   uyari    E_WARNING, sonuc null
dizgi uzerinde erisim  durdurur TypeError

Birinci satır kursun eksik ic anahtar gözlemidir: $kayitlar[0]['soyad'] değeri null verdi ve E_WARNING bildirdi. Sınıf uyarı. Yürütme sürdü; sonuç null olduğu için çağıran kod hata olduğunu ancak değerle birlikte gelen uyarıdan anlar. Önceki dersin sessizliğiyle bu ders arasındaki ayrım budur: anahtar yazımındaki kayıp duyulmuyordu, anahtar okumadaki kayıp duyuluyor.

İkinci satır dış düzeyden eksik bir indeks için aynı sonucu veriyor, üçüncü satır ise zincirin ilk halkası zaten null iken ikinci erişimin de aynı seviyeyi bildirdiğini gösteriyor: null üzerinde dizi erişimi hata değil uyarıdır ve null yine null döner. Zincir ne kadar uzarsa uzasın bir kopukluk sonrasındaki her erişim sessizce null’a çıkar; kopukluğun hangi halkada olduğu uyarı metninden okunur, sonuçtan okunmaz. Dördüncü satır sınırı gösteriyor: yol bir dizgiye vardığında ve dizgiye 'x' gibi bir dizgi anahtarı uygulandığında null dönmez, TypeError fırlar; sınıf durdurur. Uyarı kanalı yalnız null ya da eksik anahtar için işler, tipi yanlış bir değere uygulanan erişim için işlemez.

DZ7. Uyarının ileti metni ve satır bilgisi ortama bağlıdır ve ders onları basmaz; yalnız seviye adı (E_WARNING) ve sonucun tipi basılır. Aynı kopukluğun bir ayarla durduran bir hataya çevrilmesi mümkündür, ancak ders hiçbir ini değerine güvenmez ve bu yolu ölçmez.

Boşluk Birleştirme ile Sessizlik

Boşluk birleştirme işleci (null coalescing, ??) sol tarafın değerini, o null ya da tanımsız ise sağ tarafı verir. Eksik yol üzerinde uygulandığında zincirin her halkasındaki uyarıyı bastırır.

<?php
// birlestirme.php — ?? eksik yolu sessize cevirir
require 'kanal.php';

$veri = ['a' => 1];
$kayitlar = [['ad' => 'Ada']];

$gozlemler = [
    'uyarili okuma'     => fn() => $veri['b'],
    'birlestirmeli'     => fn() => $veri['b'] ?? 0,
    'derin birlestirme' => fn() => $kayitlar[3]['notlar']['fen'] ?? -1,
    'var olan deger'    => fn() => $kayitlar[0]['ad'] ?? 'yok',
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-19s%-9s%s\n", $ad, $sinif, $kanit);
}
uyarili okuma      uyari    E_WARNING, sonuc null
birlestirmeli      sessiz   sonuc int 0
derin birlestirme  sessiz   sonuc int -1
var olan deger     sessiz   sonuc string Ada

Aynı eksik anahtar iki kanalda duyuldu: yalın okuma uyarı, ?? ile sessiz. Üçüncü satır daha önemli: ?? yalnız son halkayı değil, yolun tamamını sarar; üç düzeyli bir yolda herhangi bir halka eksikse sonuç varsayılan değerdir ve hiçbir kanaldan haber gelmez. Bu işlecin amacıdır, ancak amaç sorunun kaybolması anlamına da gelir: yol yanlış yazılmışsa ('not' yerine 'notlar'), ?? yazımı hatayı da aynı sessizlikle örter.

Kayıt Listesi ile Çalışmak

İç içe diziler pratikte çoğu zaman bir kayıt listesi biçimindedir: dış dizi indeksli, her eleman aynı anahtarları taşıyan ilişkisel bir dizi. Bu biçim için üç sık iş vardır: bir sütunu çekmek, kayıtları bir anahtara göre sıralamak, kayıtları o sütuna göre indekslemek.

<?php
// kayitlar.php — kayit listesinde sutun, siralama ve indeksleme
$kayitlar = [
    ['id' => 7, 'ad' => 'Can', 'yas' => 31],
    ['id' => 3, 'ad' => 'Ada', 'yas' => 36],
    ['id' => 9, 'ad' => 'Efe', 'yas' => 24],
];

echo 'adlar: ', implode(',', array_column($kayitlar, 'ad')), "\n";
echo 'id ile indeksli: ', implode(',', array_keys(array_column($kayitlar, 'ad', 'id'))), "\n";

usort($kayitlar, fn($sol, $sag) => $sol['yas'] <=> $sag['yas']);
echo 'yasa gore: ', implode(',', array_column($kayitlar, 'ad')), "\n";

$kayitlar[1]['yas'] += 1;
echo 'guncel yas: ', $kayitlar[1]['yas'], "\n";
echo 'ortalama yas: ', array_sum(array_column($kayitlar, 'yas')) / count($kayitlar), "\n";
adlar: Can,Ada,Efe
id ile indeksli: 7,3,9
yasa gore: Efe,Can,Ada
guncel yas: 32
ortalama yas: 30.666666666667

array_column bir sütunu düz bir diziye çevirir; üçüncü argüman verilirse sonucun anahtarları o sütundan gelir. usort kayıtları verilen karşılaştırmaya göre yeniden diziler ve anahtarları yeniden numaralar. Ardından $kayitlar[1]['yas'] += 1 yazımı iki düzeyli bir yola yazıyor; yol var olduğu için hiçbir şey bildirilmiyor. Kayıt listesi ile çalışan kodun çoğu bu üç fonksiyonla ve iki düzeyli indekslemeyle yazılır; hata, kayıtlar aynı biçimde olmadığında ortaya çıkar.

DZ8. Kayıtların biçimi bu derste kod içinde sabittir. Aynı listeyi bir dosyadan ya da bir istekten okuyan kodun kayıt biçimini doğrulaması gerekir; bu doğrulama burada yapılmaz; kayıt biçimini sınayan fonksiyon bu dersin kapsamı dışındadır.

Bir kaydın bir anahtarı eksik olduğunda, sütunu çekmek ile her kayıttan anahtarı tek tek okumak ayrı kanallardan duyulur.

<?php
// eksiksutun.php — array_column eksik anahtari sessizce atlar
require 'kanal.php';

$kayitlar = [
    ['ad' => 'Ada', 'soyad' => 'Yilmaz'],
    ['ad' => 'Can'],
    ['ad' => 'Efe', 'soyad' => 'Kaya'],
];

$gozlemler = [
    'array_column' => fn() => array_column($kayitlar, 'soyad'),
    'tek tek okuma' => fn() => array_map(fn($k) => $k['soyad'], $kayitlar),
    'birlesimli okuma' => fn() => array_map(fn($k) => $k['soyad'] ?? '-', $kayitlar),
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-17s%-9s%s\n", $ad, $sinif, $kanit);
}

echo 'sutun uzunlugu: ', count(array_column($kayitlar, 'soyad')), "\n";
echo 'kayit sayisi: ', count($kayitlar), "\n";
array_column     sessiz   sonuc dizi(2)
tek tek okuma    uyari    E_WARNING, sonuc dizi(3)
birlesimli okuma sessiz   sonuc dizi(3)
sutun uzunlugu: 2
kayit sayisi: 3

array_column eksik anahtarı olan kaydı sessizce atladı: sütun üç yerine iki eleman taşıyor ve sessiz sınıfındadır. Tek tek okuma aynı kayıtta E_WARNING verdi ve uyarı sınıfındadır; ?? ile okuma ise yedek değeri verip sessizleşti. Üç yazım aynı eksikliğe üç farklı tepki gösterdi. Dikkat edilecek yer şudur: array_column sonucu ile kayıt listesi artık aynı uzunlukta değildir, bu yüzden ikisini indeksle eşleştiren bir kod sessizce yanlış kaydı eşleştirir.

Dizi Açma ve Özyinelemeli Birleştirme

İç içe bir dizinin parçalarını değişkenlere dağıtan dizi açma (destructuring) yazımı, yolun eksik olduğu durumda da kendi kanalını kullanır. İç içe iki dizinin birleştirilmesi ise düz birleştirmeden ayrı fonksiyonlarla yapılır.

<?php
// acma.php — dizi acma ve ozyinelemeli birlestirme
require 'kanal.php';

$gozlemler = [
    'tam acma'       => fn() => (function () { [$x, $y] = [1, 2]; return $x + $y; })(),
    'eksik indeks'   => fn() => (function () { [$x, $y] = [1]; return $y; })(),
    'eksik anahtar'  => fn() => (function () { ['ad' => $ad, 'yas' => $yas] = ['ad' => 'Ada']; return $yas; })(),
    'ic ice acma'    => fn() => (function () { [[$p, $q], [$r]] = [[1, 2], [3]]; return $p + $q + $r; })(),
    'eksik ic acma'  => fn() => (function () { [[$p], [$r]] = [[1]]; return $r; })(),
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-15s%-9s%s\n", $ad, $sinif, $kanit);
}

$sol = ['a' => ['x' => 1, 'y' => 2], 'b' => 3];
$sag = ['a' => ['y' => 20, 'z' => 30], 'b' => 4];
echo 'array_merge: ', json_encode(array_merge($sol, $sag)), "\n";
echo 'array_replace: ', json_encode(array_replace($sol, $sag)), "\n";
echo 'array_replace_recursive: ', json_encode(array_replace_recursive($sol, $sag)), "\n";
echo 'array_merge_recursive: ', json_encode(array_merge_recursive($sol, $sag)), "\n";
tam acma       sessiz   sonuc int 3
eksik indeks   uyari    E_WARNING, sonuc null
eksik anahtar  uyari    E_WARNING, sonuc null
ic ice acma    sessiz   sonuc int 6
eksik ic acma  uyari    E_WARNING, sonuc null
array_merge: {"a":{"y":20,"z":30},"b":4}
array_replace: {"a":{"y":20,"z":30},"b":4}
array_replace_recursive: {"a":{"x":1,"y":20,"z":30},"b":4}
array_merge_recursive: {"a":{"x":1,"y":[2,20],"z":30},"b":[3,4]}

Dizi açma, eksik indekste ya da eksik anahtarda E_WARNING verir ve değişken null kalır; yani doğrudan indeksle okumanın uyarı sınıfı açmada da geçerlidir. Tam eşleşen açma ve iç içe açma sessizdir.

Özyinelemeli birleştirmede dört fonksiyon üç ayrı sonuç verir. array_merge ve array_replace iç dizileri bütün olarak değiştirir, x anahtarı kaybolur. array_replace_recursive iç dizilere iner ve x’i korur. array_merge_recursive ise çakışan skaler değerleri dizi hâline getirir: y için [2,20], b için [3,4]. Toplam değerin tipinin sessizce değişmesi, sonradan b’yi sayı varsayan kodu bozabilecek bir ayrıntıdır.

Sınırlayıcı Ölçüm

Eksik yola okumak uyarı veriyordu. Aynı eksik yola yazmak ise ayrı bir kanaldır.

<?php
// yazma.php — eksik yola yazma ve okuma ayri kanal
require 'kanal.php';

$gozlemler = [
    'bos diziye derin yazma' => function () {
        $a = [];
        $a['x']['y'] = 1;
        return $a;
    },
    'ayni yoldan okuma' => function () {
        $a = [];
        return $a['x']['y'];
    },
    'varolan skalere yazma' => function () {
        $a = ['x' => 5];
        $a['x']['y'] = 1;
        return $a;
    },
];

foreach ($gozlemler as $ad => $gozlem) {
    [$sinif, $kanit] = dinle($gozlem);
    printf("%-24s%-9s%s\n", $ad, $sinif, $kanit);
}

$a = [];
$a['x']['y'] = 1;
echo 'kurulan yapi: ', json_encode($a), "\n";
bos diziye derin yazma  sessiz   sonuc dizi(1)
ayni yoldan okuma       uyari    E_WARNING, sonuc null
varolan skalere yazma   durdurur Error
kurulan yapi: {"x":{"y":1}}

Boş bir diziye $a['x']['y'] = 1 yazmak iç diziyi kendiliğinden kurar ve hiçbir seviye bildirmez: sınıf sessiz. Aynı yoldan okumak ise uyarı verir. Okuma ve yazma ayrı kanallardır; birinde duyulan sorun diğerinde duyulmaz. Üçüncü satır ayrımın sınırını çiziyor: yol üzerindeki mevcut değer bir skaler ise, yazma artık kendiliğinden kurmaz ve durdurur.

DZ9. Yazmanın sessizliği bir kolaylık olarak tasarlanmıştır ve yanlış yazılmış bir anahtarda yeni bir dal kurar: $a['notlr']['mat'] = 90 hata vermez, yanında yeni bir notlr dalı açar. Bu ders o dalın sonradan kim tarafından okunduğunu izlemez; ölçülen yalnız yazma anında hiçbir kanaldan haber gelmediğidir.

Özet

  • Çok boyutlu dizi ayrı bir tip değil, elemanları dizi olan dizidir; erişim yol boyunca anahtarların sırayla uygulanmasıdır.
  • count yalnız dış düzeyi sayar; COUNT_RECURSIVE iç dizileri hem eleman hem içerik olarak sayar.
  • Eksik iç anahtar null döndürür ve E_WARNING bildirir; bu dersin ölçü sınıfı uyarıdır (eksik ic anahtar).
  • null üzerinde dizi erişimi de E_WARNING verir ve null döner; kopukluktan sonraki her halka sessizce null üretir.
  • ?? yolun tamamını sessize çevirir; yanlış yazılmış yolu da aynı sessizlikle örter.
  • Okuma ile yazma ayrı kanaldır: eksik yoldan okumak uyarı verir, boş diziye derin yazmak iç diziyi sessizce kurar.

Sonraki Adım

Bu ders bir yolun kopukluğunu null ve uyarı olarak ölçtü. Dizi Fonksiyonları dersinde kanal değişiyor: bir eleman aramak, bulunamadığında uyarı vermiyor, bir değer dönüyor; ve o değer, bulunan ilk elemanın dönüşüyle gevşek karşılaştırmada karışabiliyor.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat