Broadcasting, yakni penerapan operasi pada setiap elemen array, ditulis dengan sintaks titik sehingga perulangan per elemen tidak perlu ditulis manual. Topik ini membahas operasi titik, broadcasting pada fungsi, pemadatan bentuk antar array, perbedaan .* dan *, serta penugasan broadcast .=. Setelah topik ini dipraktikkan, operasi vektorisasi dapat ditulis secara ringkas dan bentuk keluarannya dapat diprediksi.
Operasi titik per elemen#
Menambahkan titik sebelum operator biner apa pun mengubahnya menjadi operasi per elemen. Ekspresi a .+ b menjumlahkan setiap pasangan elemen yang seletak dan menghasilkan array baru. Tata letak yang sama berlaku untuk .-, .*, dan ./, sehingga satu aturan mencakup seluruh operator aritmetika. Bilangan tunggal boleh dicampur dengan array, dan bilangan itu diterapkan pada setiap elemen.
a = [1, 2, 3]
b = [10, 20, 30]
println(a .+ b)
println(a .- b)
println(a .* b)
println(a ./ b)
println(a .+ 100)[11, 22, 33]
[-9, -18, -27]
[10, 40, 90]
[0.1, 0.1, 0.1]
[101, 102, 103]Setiap operasi titik membentuk array baru dan tidak mengubah array aslinya. Dua array yang dioperasikan harus memiliki bentuk yang cocok, dan ketentuan bentuk tersebut dibahas pada bagian pemadatan bentuk di bawah.
Perbandingan juga dapat dibroadcast dengan titik. Hasilnya adalah array berisi Bool, yang ditampilkan println sebagai deretan 0 dan 1 berawalan Bool, dan banyaknya elemen true dihitung dengan sum.
a = [1, 2, 3]
println(a .> 2)
println(sum(a .> 2))Bool[0, 0, 1]
1Broadcasting pada fungsi#
Broadcasting tidak terbatas pada operator. Menambahkan titik sebelum pemanggilan fungsi, misalnya sqrt.(x), memanggil fungsi itu untuk setiap elemen. Bentuk f.(x) berlaku untuk fungsi apa pun, termasuk fungsi yang didefinisikan sendiri, dan fungsi berargumen banyak menerima array pada tiap argumennya sekaligus.
x = [4.0, 9.0, 16.0, 25.0]
println(sqrt.(x))
kuadrat(v) = v^2
println(kuadrat.(1:5))
println(max.(3, [1, 4, 2, 8]))[2.0, 3.0, 4.0, 5.0]
[1, 4, 9, 16, 25]
[3, 4, 3, 8]Pada pemanggilan max.(3, [1, 4, 2, 8]), bilangan 3 dipasangkan dengan setiap elemen array, sehingga hasilnya memilih nilai terbesar antara 3 dan setiap elemen. Berkas berikut merangkum operasi titik dan broadcasting fungsi menjadi program konversi suhu yang utuh.
# konversi-suhu.jl
# Mengubah pembacaan sensor suhu dari derajat Celsius ke Fahrenheit
# dan Kelvin menggunakan broadcasting.
celsius = [22.5, 24.0, 21.8, 25.3, 23.1]
fahrenheit = celsius .* 9 ./ 5 .+ 32
kelvin = celsius .+ 273.15
println("Celsius: ", celsius)
println("Fahrenheit: ", round.(fahrenheit, digits=1))
println("Kelvin: ", kelvin)Celsius: [22.5, 24.0, 21.8, 25.3, 23.1]
Fahrenheit: [72.5, 75.2, 71.2, 77.5, 73.6]
Kelvin: [295.65, 297.15, 294.95, 298.45, 296.25]Pemadatan bentuk antar array#
Bentuk dua array yang dibroadcast tidak harus sama persis. Setiap dimensi harus sama, atau salah satunya berukuran satu, dan dimensi berukuran satu menyebar sepanjang dimensi yang lebih besar. Ukuran keluaran pada setiap dimensi mengikuti nilai terbesar di antara dimensi-dimensi argumen yang bersangkutan, yaitu untuk setiap dimensi dengan argumen.
Vektor [1, 2, 3] bertindak layaknya kolom berisi tiga baris, sedangkan [10 20 30 40] yang ditulis dengan spasi merupakan matriks 1×4, yaitu vektor baris. Penjumlahan keduanya menghasilkan matriks 3×4, karena setiap elemen kolom dijumlahkan dengan setiap elemen baris.
kolom = [1, 2, 3]
baris = [10 20 30 40]
matriks = kolom .+ baris
println(matriks)
println(size(matriks))[11 21 31 41; 12 22 32 42; 13 23 33 43]
(3, 4)Diagram berikut menggambarkan penyebaran kedua vektor tersebut menjadi matriks 3×4.
Untuk mencoba aturan bentuk pada kombinasi lain, jalankan demo berikut, pilih panjang dan orientasi kedua vektor, kemudian amati bentuk keluaran atau pesan error dimensinya.
Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>Peraga pemadatan bentuk broadcasting</title>
<style>
body { font-family: system-ui, sans-serif; margin: 0; padding: 14px; color: #1a1a1a; background: #ffffff; }
.baris { display: flex; gap: 18px; flex-wrap: wrap; margin-bottom: 10px; font-size: 13.5px; }
fieldset { border: 1px solid #d6dde7; border-radius: 8px; padding: 8px 12px 10px; }
legend { font-size: 13px; font-weight: 600; padding: 0 4px; }
select { font-size: 13px; padding: 3px 5px; }
button { padding: 6px 14px; border: 0; border-radius: 6px; background: #35567e; color: #ffffff; font-size: 13.5px; cursor: pointer; }
button:hover { background: #284463; }
#keluaran { font-size: 13.5px; }
#bentuk { font-family: ui-monospace, monospace; margin-bottom: 8px; }
#bentuk.gagal { color: #a03d3d; font-weight: 600; }
table { border-collapse: collapse; }
td { border: 1px solid #46648c; min-width: 34px; height: 26px; text-align: center; font-family: ui-monospace, monospace; font-size: 12.5px; background: #e6f2e6; }
td.kolom-a { background: #e3ecf7; font-weight: 600; }
td.baris-b { background: #e3ecf7; font-weight: 600; }
.kosong { border: 0 !important; background: transparent !important; }
</style>
</head>
<body>
<div class="baris">
<fieldset>
<legend>Vektor A</legend>
<label>Panjang <select id="panjang-a"><option>1</option><option selected>3</option><option>4</option></select></label>
<label>Orientasi <select id="orientasi-a"><option value="kolom" selected>kolom</option><option value="baris">baris</option></select></label>
</fieldset>
<fieldset>
<legend>Vektor B</legend>
<label>Panjang <select id="panjang-b"><option>1</option><option selected>4</option><option>3</option></select></label>
<label>Orientasi <select id="orientasi-b"><option value="kolom">kolom</option><option value="baris" selected>baris</option></select></label>
</fieldset>
<button id="hitung" type="button">Hitung A .+ B</button>
</div>
<div id="keluaran">
<div id="bentuk"></div>
<div id="tabel"></div>
</div>
<script>
function broadcast() {
var ma = Number(document.getElementById('panjang-a').value);
var mb = Number(document.getElementById('panjang-b').value);
var kolomA = document.getElementById('orientasi-a').value === 'kolom';
var kolomB = document.getElementById('orientasi-b').value === 'kolom';
var a = [10, 20, 30, 40].slice(0, ma);
var b = [1, 2, 3, 4].slice(0, mb);
var dimA = kolomA ? [ma, 1] : [1, ma];
var dimB = kolomB ? [mb, 1] : [1, mb];
var bentuk = document.getElementById('bentuk');
var tabel = document.getElementById('tabel');
tabel.innerHTML = '';
bentuk.className = '';
var cocok1 = dimA[0] === dimB[0] || dimA[0] === 1 || dimB[0] === 1;
var cocok2 = dimA[1] === dimB[1] || dimA[1] === 1 || dimB[1] === 1;
if (!cocok1 || !cocok2) {
bentuk.className = 'gagal';
bentuk.textContent = 'Dimensi tidak cocok: A (' + dimA.join('×') + ') dan B (' + dimB.join('×') +
'), ada dimensi yang berbeda dan tidak ada yang bernilai 1.';
return;
}
var baris = Math.max(dimA[0], dimB[0]);
var kolom = Math.max(dimA[1], dimB[1]);
bentuk.textContent = 'Bentuk keluaran: ' + baris + '×' + kolom;
var el = document.createElement('table');
var trJudul = document.createElement('tr');
trJudul.appendChild(sel('', 'kosong'));
for (var j = 0; j < kolom; j++) {
var vb = kolomB ? 1 : b[j % b.length];
trJudul.appendChild(sel(String(vb), 'baris-b'));
}
el.appendChild(trJudul);
for (var i = 0; i < baris; i++) {
var tr = document.createElement('tr');
var va = kolomA ? a[i % a.length] : 10;
tr.appendChild(sel(String(va), 'kolom-a'));
for (var k = 0; k < kolom; k++) {
var vb2 = kolomB ? 1 : b[k % b.length];
tr.appendChild(sel(String(va + vb2), ''));
}
el.appendChild(tr);
}
tabel.appendChild(el);
}
function sel(teks, kelas) {
var td = document.createElement('td');
if (kelas) { td.className = kelas; }
td.textContent = teks;
return td;
}
document.getElementById('hitung').addEventListener('click', broadcast);
</script>
</body>
</html>Perbedaan .* dan *#
Operator .* dan * berbeda semantik. .* mengalikan setiap pasangan elemen yang seletak, sedangkan * merupakan perkalian matriks dari aljabar linear yang menjumlahkan hasil kali baris dan kolom. Untuk dua matriks persegi berukuran sama, keduanya sama-sama sah secara sintaks, tetapi hasilnya berbeda, dan perbedaan inilah sumber kesalahan yang tidak menimbulkan pesan apa pun.
a = [1 2; 3 4]
println(a .* a)
println(a * a)[1 4; 9 16]
[7 10; 15 22]Perbedaannya juga terasa pada vektor. a .* b selalu menghasilkan vektor baru untuk dua vektor dengan panjang sama, sedangkan a * b tanpa transpos tidak terdefinisi dan berhenti dengan MethodError. Sebagai aturan praktis, gunakan .* untuk operasi per elemen dan * hanya apabila perkalian matriks memang dimaksudkan.
Penugasan broadcast dengan .=#
Operator .= menuliskan hasil broadcast ke array yang sudah ada tanpa membentuk array baru. Pola ini dipakai untuk mengisi atau memperbarui isi array, misalnya x .= 0 untuk mengosongkan seluruh elemen. Bentuk gabungan seperti .+= juga tersedia dan memperbarui array yang sama di tempat.
x = [1.0, 2.0, 3.0]
x .= 0
println(x)
y = [10.0, 20.0, 30.0]
y .+= 5
println(y)[0.0, 0.0, 0.0]
[15.0, 25.0, 35.0]Array tujuan wajib sudah dialokasikan dengan bentuk yang sama dengan hasil broadcast. Apabila bentuknya berbeda, program berhenti dengan error dimensi, bukan membentuk array baru menggantikannya. Perilaku alokasi dari penugasan broadcast dibahas lebih lanjut pada topik mekanika broadcast.
Kesalahan umum#
Kesalahan berikut paling sering muncul saat topik ini dipraktikkan. Yang pertama dapat direproduksi dan ditangkap pada contoh berikut.
try
a = [1, 2, 3]
b = [1, 2]
println(a .+ b)
catch e
println(typeof(e))
endDimensionMismatch- Membroadcast array dengan dimensi yang tidak cocok. Dimensi harus sama atau berukuran satu, sehingga
[1, 2, 3] .+ [1, 2]berhenti denganDimensionMismatch. Perbaikannya dengan menyelaraskan bentuk argumen, misalnya membentuk vektor baris atau memotong datanya. - Menukar
*dengan.*. Pada matriks persegi, kedua operator sama-sama berjalan tanpa pesan, tetapi hasilnya berbeda sama sekali. Periksa kembali maksud operasinya sebelum memilih operator perkalian. - Mengira
x += 1danx .+= 1berperilaku sama.x += 1membentuk array baru lalu mengikat ulang namax, sedangkanx .+= 1memperbarui isi array yang sama. Perbedaannya terasa apabila array yang sama juga dirujuk melalui variabel lain.
Ringkasan#
- Titik sebelum operator atau fungsi, misalnya
.+dansqrt.(x), menerapkan operasi per elemen dan menghasilkan array baru. - Setiap dimensi argumen harus sama atau berukuran satu, dan dimensi berukuran satu menyebar, sehingga vektor kolom 3×1 dan vektor baris 1×4 menghasilkan matriks 3×4.
.*mengalikan per elemen, sedangkan*merupakan perkalian matriks dari aljabar linear..=beserta bentuk gabungannya memperbarui array yang sudah ada di tempat tanpa membentuk array baru.
Referensi#
- Mathematical Operations and Elementary Functions — The Julia Language — Mendaftar operator aritmetika Julia beserta sintaks operasi vektorisasinya.
- Arrays · Broadcasting — The Julia Language — Mendokumentasikan broadcast, broadcast!, dan aturan bentuk keluarannya.