Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Currying i częściowa aplikacja

Wyobraź sobie system karmienia dinozaurów w parku. Każdy gatunek ma swoje wymagania - rodzaj pożywienia, porcję i porę karmienia. Zamiast za każdym razem podawać wszystkie parametry, możemy przygotować funkcje "wstępnie skonfigurowane" dla danego gatunku. To właśnie robi currying - przekształca funkcję wieloargumentową w sekwencję funkcji jednoargumentowych.

Czym jest currying?

Currying to technika przekształcania funkcji przyjmującej wiele argumentów w sekwencję funkcji, z których każda przyjmuje dokładnie jeden argument.

1// Zwykla funkcja z 3 argumentami
2function feedDinosaur(species, food, portion) {
3  return species + ' dostaje ' + portion + 'kg ' + food;
4}
5feedDinosaur('T-Rex', 'meat', 50);
6
7// Wersja curried - sekwencja funkcji jednoargumentowych
8function feedDinosaurCurried(species) {
9  return function(food) {
10    return function(portion) {
11      return species + ' dostaje ' + portion + 'kg ' + food;
12    };
13  };
14}
15feedDinosaurCurried('T-Rex')('meat')(50);
16
17// To samo ze strzalkami (bardziej zwiezle)
18const feedCurried = (species) => (food) => (portion) =>
19  species + ' dostaje ' + portion + 'kg ' + food;

Praktyczne zastosowanie curryingu

Prawdziwa siła curryingu tkwi w tworzeniu wyspecjalizowanych funkcji:

1const feedCurried = (species) => (food) => (portion) =>
2  ({ species, food, portion, timestamp: Date.now() });
3
4// Specjalizowane funkcje dla gatunkow
5const feedRex = feedCurried('T-Rex');
6const feedRexMeat = feedRex('meat');
7
8// Uzycie wyspecjalizowanych funkcji
9feedRexMeat(50);  // { species: 'T-Rex', food: 'meat', portion: 50, ... }
10feedRexMeat(30);  // { species: 'T-Rex', food: 'meat', portion: 30, ... }
11
12// Mozna tez dla innego gatunku
13const feedBrachio = feedCurried('Brachiosaurus');
14const feedBrachioPlants = feedBrachio('plants');
15feedBrachioPlants(200); // { species: 'Brachiosaurus', food: 'plants', portion: 200, ... }

Uniwersalna funkcja curry

Możemy napisać uniwersalną funkcję

curry
, która automatycznie przekształca dowolną funkcję:

1function curry(fn) {
2  return function curried(...args) {
3    if (args.length >= fn.length) {
4      return fn(...args);
5    }
6    return (...moreArgs) => curried(...args, ...moreArgs);
7  };
8}
9
10// Uzycie
11function createEnclosure(zone, type, capacity, voltage) {
12  return { zone, type, capacity, voltage };
13}
14
15const curriedCreate = curry(createEnclosure);
16
17// Mozna wywolac na rozne sposoby:
18curriedCreate('A')('carnivore')(5)(10000);
19curriedCreate('A', 'carnivore')(5, 10000);
20curriedCreate('A', 'carnivore', 5)(10000);
21curriedCreate('A', 'carnivore', 5, 10000);
22// Wszystkie wywolania daja ten sam wynik

Częściowa aplikacja (Partial Application)

Częściowa aplikacja to pokrewna technika - "zamraża" część argumentów funkcji, tworząc nową funkcję z mniejszą liczbą parametrów.

1function partial(fn, ...presetArgs) {
2  return (...laterArgs) => fn(...presetArgs, ...laterArgs);
3}
4
5// Funkcja logowania incydentow
6function logIncident(severity, zone, message) {
7  return '[' + severity + '] Zone ' + zone + ': ' + message;
8}
9
10// Wyspecjalizowane loggery
11const logCritical = partial(logIncident, 'CRITICAL');
12const logWarning = partial(logIncident, 'WARNING');
13const logCriticalZoneA = partial(logIncident, 'CRITICAL', 'A');
14
15logCritical('B', 'Fence breach detected');
16// '[CRITICAL] Zone B: Fence breach detected'
17
18logCriticalZoneA('T-Rex escaped!');
19// '[CRITICAL] Zone A: T-Rex escaped!'

Currying vs częściowa aplikacja

Choć obie techniki tworzą wyspecjalizowane funkcje, różnią się sposobem działania:

  • Currying: zawsze tworzy łańcuch funkcji jednoargumentowych
  • Częściowa aplikacja: "zamraża" dowolną liczbę argumentów naraz
Ir a CodeWorlds