---
title: 'Yüksek Mertebeden Fonksiyonlar'
source: 'https://academia.sh/tr/kurslar/paradigmalar/yuksek-mertebeden-fonksiyonlar'
course: 'Programlama Paradigmaları'
language: tr
updated: '2026-08-17T18:10:41+00:00'
license: 'CC BY-SA 4.0'
---

# Yüksek Mertebeden Fonksiyonlar

Davranışın parametreleşmesi: aynı ücretlendirme kuralının bayrak parametresiyle, arama tablosuyla ve fonksiyon parametresiyle yazılması, yeni bir kural eklendiğinde üç sürümde dokunulan satırın ve çekirdekteki karar noktasının sayılması.

Önceki iki ders veriyi ele aldı: ücret hesabı girdiden çıktıya bir eşlemeye indirgendi,
tarife değiştirilemez hâle getirildi. Davranış ise sabit kaldı. `ucretHesapla` tek bir
kural kümesini kendi gövdesinde taşıyor; sözleşmeli indirim, hacim indirimi, gece teslimi
eki ve müşteriye özel anlaşmalar eklendikçe o gövde büyüyecek.

Bu ders davranışın kendisini parametreye çevirir. Aynı ücretlendirme mantığı üç ayrı yolla
yazılır: bayrak parametresiyle, ad-oran tablosuyla ve fonksiyon parametresiyle. Sonra
gerçek bir gereksinim gelir — gece teslimi eki — ve her üç sürümde dokunulan satır ile
çekirdekte biriken karar noktası sayılır.

## Ortak Çekirdek

Üç sürüm de aynı temel ücretten başlar. Tarife verisi ve indirimsiz hesap tek bir dosyada
durur; kurallar bu hesabın üzerine gelir.

```js
// tarife.mjs — tarife verisi ve indirimsiz temel ucret
export const TARIFE = {
  ad: "yurtici-standart",
  asgariUcret: 4990,
  kademeler: [
    { ustAgirlikGram: 1000, ucret: 4990 },
    { ustAgirlikGram: 5000, ucret: 6490 },
    { ustAgirlikGram: 15000, ucret: 10900 },
  ],
  bolgeKatsayisi: { 1: 1.0, 2: 1.15, 3: 1.35 },
};

export const GONDERILER = [
  { kod: "TR-4471", agirlikGram: 800, bolge: 1 },
  { kod: "TR-4472", agirlikGram: 3200, bolge: 2 },
  { kod: "TR-4473", agirlikGram: 12400, bolge: 3 },
];

export function temelUcret(gonderi, tarife) {
  const kademe = tarife.kademeler.find((k) => gonderi.agirlikGram <= k.ustAgirlikGram)
    ?? tarife.kademeler.at(-1);
  return Math.max(tarife.asgariUcret,
    Math.round(kademe.ucret * tarife.bolgeKatsayisi[gonderi.bolge]));
}
```

Değişen kısım kurallardır: hangi indirimin hangi gönderiye uygulanacağı. Bu karar üç ayrı
yerde verilebilir.

## Aynı Davranışın Üç Yazılışı

Birinci yol kararı **bayrak parametresine** bırakır. Çağıran hangi kuralın işleyeceğini
mantıksal değerlerle bildirir, kuralın kendisi çekirdeğin gövdesindedir.

```js
// bayrak.mjs — hangi kuralin isleyecegi bayrak parametreleriyle secilir
import { temelUcret } from "./tarife.mjs";

export function ucretBayrakli(gonderi, tarife, secenek = {}) {
  let ucret = temelUcret(gonderi, tarife);
  if (secenek.sozlesmeli) ucret = Math.round(ucret * 0.88);
  if (secenek.hacim) ucret = Math.round(ucret * 0.95);
  return ucret;
}
```

İkinci yol kuralları bir **arama tablosunda** toplar. Çekirdek artık her kural için ayrı
bir dal taşımaz; adı tablodan arar, bulduğu oranı uygular.

```js
// tablo.mjs — kurallar ad-oran tablosunda tutulur, adla secilir
import { temelUcret } from "./tarife.mjs";

const ORANLAR = {
  sozlesmeli: 0.88,
  hacim: 0.95,
};

export function ucretTabloyla(gonderi, tarife, adlar = []) {
  let ucret = temelUcret(gonderi, tarife);
  for (const ad of adlar) ucret = Math.round(ucret * (ORANLAR[ad] ?? 1));
  return ucret;
}
```

Üçüncü yol kuralı bir değere değil, doğrudan bir **fonksiyona** çevirir ve o fonksiyonu
parametre olarak alır.

```js
// kural.mjs — kural bir fonksiyondur, cagirana parametre olarak gecirilir
import { temelUcret } from "./tarife.mjs";

export const sozlesmeli = (ucret) => Math.round(ucret * 0.88);
export const hacim = (ucret) => Math.round(ucret * 0.95);

export function ucretKurallarla(gonderi, tarife, kurallar = []) {
  return kurallar.reduce((ucret, kural) => kural(ucret, gonderi),
    temelUcret(gonderi, tarife));
}
```

Üçüncü sürümdeki `ucretKurallarla` bir **yüksek mertebeden fonksiyondur (higher-order
function)**: Programlama Temelleri kursunda tanımlandığı gibi, ya bir fonksiyonu parametre
olarak alan ya da bir fonksiyon döndüren fonksiyon. Burada iki kez geçer — hem
`ucretKurallarla` kural listesini parametre olarak alır, hem de gövdesindeki `reduce`
kendisine verilen birleştirme fonksiyonunu çağırır.

Aradaki fark biçimsel değildir. Bayraklı sürümde ne yapılacağı çekirdekte yazılıdır,
çağıran yalnızca hangisinin açık olduğunu söyler. Tablolu sürümde ne yapılacağı yine
kitaplığın içindedir, ama tek bir veri yapısında toplanmıştır. Kurallı sürümde ne
yapılacağı çekirdeğin dışındadır; çekirdek yalnızca sırayla uygulamayı bilir.

## Yeni Bir Kural Eklendiğinde

Gereksinim şu: gece teslimi seçilen gönderilere sabit bir ek uygulanacak, ek 5 kilogramın
altındaki gönderilerde 1500 kuruş, üstündekilerde 2500 kuruş olacak. Bu kural bir çarpan
değildir; toplanan bir tutardır ve gönderinin ağırlığına bakar.

```js
// bayrak2.mjs — bayrakli surume gece teslimi eki eklendi
import { temelUcret } from "./tarife.mjs";

export function ucretBayrakli(gonderi, tarife, secenek = {}) {
  let ucret = temelUcret(gonderi, tarife);
  if (secenek.sozlesmeli) ucret = Math.round(ucret * 0.88);
  if (secenek.hacim) ucret = Math.round(ucret * 0.95);
  if (secenek.gece) ucret += gonderi.agirlikGram > 5000 ? 2500 : 1500;
  return ucret;
}
```

```js
// tablo2.mjs — tablolu surume gece teslimi eki eklendi
import { temelUcret } from "./tarife.mjs";

const ORANLAR = {
  sozlesmeli: { oran: 0.88, ek: 0, agirEk: 0 },
  hacim: { oran: 0.95, ek: 0, agirEk: 0 },
  gece: { oran: 1, ek: 1500, agirEk: 2500 },
};

export function ucretTabloyla(gonderi, tarife, adlar = []) {
  let ucret = temelUcret(gonderi, tarife);
  for (const ad of adlar) {
    const k = ORANLAR[ad] ?? { oran: 1, ek: 0, agirEk: 0 };
    ucret = Math.round(ucret * k.oran);
    ucret += gonderi.agirlikGram > 5000 ? k.agirEk : k.ek;
  }
  return ucret;
}
```

```js
// kural2.mjs — kurallı surume gece teslimi eki eklendi
import { temelUcret } from "./tarife.mjs";

export const sozlesmeli = (ucret) => Math.round(ucret * 0.88);
export const hacim = (ucret) => Math.round(ucret * 0.95);
export const gece = (ucret, g) => ucret + (g.agirlikGram > 5000 ? 2500 : 1500);

export function ucretKurallarla(gonderi, tarife, kurallar = []) {
  return kurallar.reduce((ucret, kural) => kural(ucret, gonderi),
    temelUcret(gonderi, tarife));
}
```

Üç sürümün de aynı sonucu ürettiği koşturularak doğrulanır. Ölçüm dosyası altı çağrıyı yan
yana koyar: önce eski üç sürüm, sonra gece eki eklenmiş üç sürüm.

```js
// olcum.mjs — uc surum ayni sonucu mu veriyor, yeni kural eklenince ne degisiyor
import { TARIFE, GONDERILER } from "./tarife.mjs";
import { ucretBayrakli } from "./bayrak.mjs";
import { ucretTabloyla } from "./tablo.mjs";
import { ucretKurallarla, sozlesmeli, hacim } from "./kural.mjs";
import { ucretBayrakli as bayrakYeni } from "./bayrak2.mjs";
import { ucretTabloyla as tabloYeni } from "./tablo2.mjs";
import { ucretKurallarla as kuralYeni, gece } from "./kural2.mjs";

const her = (f) => GONDERILER.map(f).join(", ");
const satir = (ad, deger) => console.log(`${ad.padEnd(20)} ${deger}`);

satir("bayrak / once", her((g) => ucretBayrakli(g, TARIFE, { sozlesmeli: true, hacim: true })));
satir("tablo  / once", her((g) => ucretTabloyla(g, TARIFE, ["sozlesmeli", "hacim"])));
satir("kural  / once", her((g) => ucretKurallarla(g, TARIFE, [sozlesmeli, hacim])));

const secenek = { sozlesmeli: true, hacim: true, gece: true };
const adlar = ["sozlesmeli", "hacim", "gece"];
satir("bayrak / gece ile", her((g) => bayrakYeni(g, TARIFE, secenek)));
satir("tablo  / gece ile", her((g) => tabloYeni(g, TARIFE, adlar)));
satir("kural  / gece ile", her((g) => kuralYeni(g, TARIFE, [sozlesmeli, hacim, gece])));

// Yalnizca bir cagirani ilgilendiren kural: kitaplikta hicbir dosya degismez.
const ilkGonderi = (ucret, g) => (g.kod === "TR-4471" ? Math.round(ucret * 0.5) : ucret);
satir("kural  / cagirandan", her((g) => kuralYeni(g, TARIFE, [sozlesmeli, hacim, gece, ilkGonderi])));
```

```
bayrak / once        4171, 6239, 12302
tablo  / once        4171, 6239, 12302
kural  / once        4171, 6239, 12302
bayrak / gece ile    5671, 7739, 14802
tablo  / gece ile    5671, 7739, 14802
kural  / gece ile    5671, 7739, 14802
kural  / cagirandan  2836, 7739, 14802
```

İlk altı satır üç sürümün davranışça denk olduğunu gösterir. Son satır yalnızca kurallı
sürümde yazılabilen bir şeyi gösterir: yalnızca `TR-4471` kodlu gönderiyi ilgilendiren bir
indirim, ölçüm dosyasında iki satırla tanımlanıp geçirilmiştir. Kitaplıkta hiçbir dosya
değişmemiştir. Bayraklı ya da tablolu sürümde aynı kuralın çalışması için tek bir müşteriye
ait bir koşulun kitaplığın içine yazılması gerekirdi.

## Dokunulan Satırın Sayılması

Gece eki eklenirken her sürümde ne kadar yer değiştiğini `diff` sayar. İki ölçüt ayrı
tutulur: dosyanın tamamında dokunulan satır ve **çekirdekte** — yani dışa verilen
ücretlendirme fonksiyonunun kendisinde — dokunulan satır. Üçüncü ölçüt, çekirdekte biriken
karar noktası sayısıdır.

```sh
# Cekirdek: dosyadaki ilk `export function` satirindan dosya sonuna kadar olan bolum.
# Ilk satirdaki dosya adi yorumu olcume katilmaz.
cekirdek() { awk '/^export function/,0' "$1"; }
karar() { cekirdek "$1" | grep -oE '\bif\b|\bfor\b|\?\?|\?' | wc -l | tr -d ' '; }
for s in bayrak tablo kural; do
  toplam=$(diff <(tail -n +2 "$s.mjs") <(tail -n +2 "${s}2.mjs") | grep -c '^[<>]')
  ic=$(diff <(cekirdek "$s.mjs") <(cekirdek "${s}2.mjs") | grep -c '^[<>]')
  printf '%-7s dokunulan: %2d  cekirdekte: %d  karar noktasi: %s -> %s\n' \
    "$s" "$toplam" "$ic" "$(karar "$s.mjs")" "$(karar "${s}2.mjs")"
done
```

```
bayrak  dokunulan:  1  cekirdekte: 1  karar noktasi: 2 -> 4
tablo   dokunulan: 11  cekirdekte: 6  karar noktasi: 2 -> 3
kural   dokunulan:  1  cekirdekte: 0  karar noktasi: 0 -> 0
```

Toplam satır sayısı yanıltıcı olabilir: bayraklı sürüm de kurallı sürüm de tek satırla
büyümüştür. Ayrım ikinci sütundadır. Bayraklı sürümde o satır çekirdeğin gövdesine
girmiştir; karar noktası ikiden dörde çıkmıştır ve her yeni kural bu sayıyı artırmaya devam
edecektir. Kurallı sürümde çekirdek hiç değişmemiştir; eklenen satır çekirdeğin dışında,
bağımsız bir fonksiyondur ve çekirdeğin karar noktası sıfırda kalır.

Tablolu sürüm en pahalı olanıdır: on bir satır dokunulmuş, çekirdeğin gövdesinde altı satır
değişmiştir. Nedeni tablonun **şemasıdır**. Tablo yalnızca oran tutabildiği için, toplanan
bir ek gelince her satıra iki alan daha eklenmiştir — `sozlesmeli` ile `hacim` bu alanları
hiç kullanmasa da. Arama tablosu, tuttuğu değerlerin hepsi aynı biçimde olduğu sürece
ucuzdur; yeni bir kural yeni bir biçim getirdiğinde bedel tablonun tamamına yayılır.

## Fonksiyon Döndüren Fonksiyon

Yüksek mertebeliliğin ikinci biçimi, fonksiyon döndürmektir. Kuralların hepsi birkaç
kalıptan türediği için, kuralları elle yazmak yerine üreten fonksiyonlar tanımlanabilir.

```js
// uretici.mjs — fonksiyon donduren fonksiyon: yuksek mertebenin ikinci yuzu
import { TARIFE, GONDERILER } from "./tarife.mjs";
import { ucretKurallarla } from "./kural2.mjs";

export const oranli = (oran) => (ucret) => Math.round(ucret * oran);
export const sabitEk = (kurus) => (ucret) => ucret + kurus;
export const kosullu = (kosul, kural) => (ucret, g) => (kosul(g) ? kural(ucret, g) : ucret);

const agir = (g) => g.agirlikGram > 5000;
const kurallar = [
  oranli(0.88),
  oranli(0.95),
  kosullu(agir, sabitEk(2500)),
  kosullu((g) => !agir(g), sabitEk(1500)),
];

console.log(GONDERILER.map((g) => ucretKurallarla(g, TARIFE, kurallar)).join(", "));
```

```
5671, 7739, 14802
```

Sonuç, elle yazılmış üç kuralın ürettiği değerlerle birebir aynıdır. Fark üretim
biçimindedir: üç satırlık üç üreticiden — `oranli`, `sabitEk` ve `kosullu` — sınırsız
sayıda kural kurulabilir. `kosullu` ayrıca bir kuralı başka bir kuralla sarmalar; aldığı
kuralı çağırıp çağırmamaya karar verir. Kuralların bu biçimde birbirinin üstüne
yığılabilmesi bir sonraki dersin konusudur.

## Dolaylılığın Bedeli

Fonksiyon parametresi bedelsiz değildir. "Sözleşmeli bir müşterinin gönderisinde ne
oluyor" sorusunun yanıtı bayraklı sürümde tek dosyadadır: `bayrak2.mjs` okunur, üç dal
görülür, iş biter. Kurallı sürümde yanıt iki dosyaya dağılmıştır — kuralın tanımı
`kural2.mjs` içinde, hangi kuralların hangi sırayla uygulandığı `olcum.mjs` içindedir.
Okunacak dosya sayısı birden ikiye çıkar ve çağrı yerlerinin sayısı arttıkça sıranın nerede
kurulduğu sorusu her çağrı yerine ayrı ayrı sorulur.

Ölçüt bu yüzden kuralın sayısı ve kaynağıdır. Kural kümesi kapalı ve kısa ise — iki üç
sabit seçenek, hepsi kitaplığın kendi bilgisi — bayrak daha az dolaylılıkla aynı işi
görür. Kural kümesi açık ise, yani kitaplığın önceden bilemeyeceği kurallar çağıranlardan
gelecekse, fonksiyon parametresi çekirdeği hiç değiştirmeden bunu karşılayan tek sürümdür.

## Özet

- Yüksek mertebeden fonksiyon, bir fonksiyonu parametre olarak alan ya da bir fonksiyon
  döndüren fonksiyondur; `ucretKurallarla` birinci biçimi, `oranli` ikinci biçimi gösterir.
- Aynı davranış üç yolla yazıldığında sonuçlar birebir aynıdır: üç gönderi için 4171, 6239
  ve 12302.
- Gece eki eklendiğinde bayraklı sürümde çekirdekte 1 satır değişir ve karar noktası 2'den
  4'e çıkar; kurallı sürümde çekirdekte 0 satır değişir ve karar noktası 0'da kalır.
- Arama tablosu, yeni kural tablonun şemasına uymadığında en pahalı sürümdür: 11 satır
  dokunulmuş, çekirdekte 6 satır değişmiştir.
- Yalnızca bir çağıranı ilgilendiren kural, kurallı sürümde iki satırla tanımlanıp geçirilir
  ve kitaplıkta hiçbir dosya değişmez.
- Fonksiyon parametresinin bedeli dolaylılıktır: bir gönderiye ne olduğunu anlamak için
  okunacak dosya sayısı birden ikiye çıkar.

## Sonraki Adım

Bu dersin kuralları tek tek uygulandı; `reduce` her birini sırayla çağırdı ve ara sonuçlar
hiçbir yerde adlandırılmadı. Kural sayısı arttıkça asıl soru sıranın kendisi olur:
indirimin ekten önce mi sonra mı işlediği, asgari ücret tabanının nerede uygulandığı sonucu
değiştirir. Sonraki ders bu ardışık uygulamayı bir boru hattı olarak kurar: adımlar
adlandırılır, her adım tek tek sınanır ve iki adımın yeri değiştirildiğinde kaç gönderinin
ücretinin kaç kuruş kaydığı sayılır.
