2016-07-28 25 views
10

Kombinator K może być zaimplementowany jak poniżej i implementacja nie powinna wywoływać żadnych skutków ubocznych.Praktyczne użycie kombinatora K (jareczek) w javascript

const K = x => y => x; 

Jest czasami nazywany "const" (jak w Haskell). Funkcję K można zdefiniować jako "pobiera wartość i zwraca (stałą) unarną funkcję, która zawsze zwraca tę wartość."

Kiedy to jest przydatne? Proszę, pomóżcie mi w praktycznych przykładach.

+0

http://cs.stackexchange.com/questions/55441/what-is-the-purpose-of-the-ski-combinator-calculusor-even-lambda-calculus- wha –

+0

Wygląda jak funkcja CL [constanty] (http://clhs.lisp.se/Body/f_cons_1.htm), która czasami jest przydatna. – Sylwester

+3

W językach curried użyjesz go, gdy chcesz, aby wywołanie zwrotne zignorowało pierwszy argument. – Bergi

Odpowiedz

2

Problem z K, podobnie jak w przypadku wszystkich prymitywnych kombinatorów, polega na tym, że nie można go uznać za samodzielny. Prymitywne kombinatory są podstawowymi elementami składowymi programowania funkcjonalnego. Potrzebujesz odpowiedniego kontekstu, aby obejrzeć je w pracy. Wyzwanie polega na tym, by zrozumieć ten kontekst, jeśli dopiero zaczynasz funkcjonalny paradygmat.

Oto "typowy kontekst": Option. Instancje typu Option są jak wartości, które mogą być null, ale nigdy nie wygeneruje błąd podczas stosowania funkcji:

// the option type 
 

 
const Option = { 
 
    some: Symbol.for("ftor/Option.some"), 
 

 
    none: Symbol.for("ftor/Option.none"), 
 

 
    of: x => factory(Option.some) (x), 
 

 
    cata: pattern => o => pattern[o.tag](o.x), 
 

 
    fold: f => g => o => Option.cata({[Option.some]: f, [Option.none]: g}) (o), 
 

 
    map: f => o => Option.fold(x => Option.of(f(x))) (K(o)) (o) 
 
    //            ^^^^ 
 
} 
 

 

 
// a generic map function 
 

 
const map = type => f => o => type.map(f) (o); 
 

 

 
// functor factory 
 

 
const factory = tag => value => (
 
    {x: value === undefined ? null : value, tag: tag} 
 
); 
 

 

 
// auxiliary functions 
 

 
const K = x => y => x; 
 
const sqr = x => x * x; 
 

 

 
// a few data to play around 
 

 
const o = factory(Option.some) (5); 
 
const p = factory(Option.none)(); 
 

 

 

 
// and run 
 

 
let r1 = map(Option) (sqr) (o); 
 
let r2 = map(Option) (sqr) (p); 
 

 
console.log("map over o", r1); 
 
console.log("map over p", r2);

Co K zrobić w tej realizacji? Przeanalizujmy zasadniczą linię:

f => o => Option.fold(x => Option.of(f(x))) (K(o)) (o) 

Option.fold spodziewa dwie funkcje. Pierwsza przeszła funkcja x => Option.of(f(x)) jest dla przypadku some (istnieje wartość). Drugi K(o) dla przypadku none (nie ma wartości). Przypomnijmy, że K oczekuje dwóch argumentów: K = x => y => {return x}. K(o) przypisuje o do x. Bez względu na to, co jest przekazywane jako drugi argument, K zawsze będzie ignorować y i zamiast tego zwróci x.

Co jednak oznacza o w wyrażeniu K(o)? Reprezentuje on Option.none, czyli brak wartości. Więc kiedy ktoś próbuje odwzorować funkcję f przez none, właśnie zwrócono none, niezależnie od tego, co f przekazuje jako drugi argument do K.

+0

Gdzie jest funkcja warunku wstępnego "precon"? –

1

Kombinator K może być również używany jako wartość prawdy, gdy używa się zakotwiczonego w kodzie Booleans. tj. IF-TEST THEN ELSE: jeśli twój "IF-TEST" zwróci K, to "else" zostanie usunięte, a "then" zostanie wykonane.

+0

dowolny fragment kodu? –

2

Rodzaj szerokiego pytania, ale miło, podoba mi się.

Aby wesprzeć mój przykład, w tej odpowiedzi mam zamiar wdrożyć & hellip;

abuild :: Number -> (Number -> a) -> [a] 

& hellip; który, jak sugeruje typ, pobiera liczbę i funkcję do zbudowania tablicy. Może to być przydatne, jeśli chcesz zbudować tablicę o znanym rozmiarze na podstawie niektórych obliczeń.


Zbudujmy tablicę z 5 elementami, korzystając z funkcji identyfikacji, id.Jak widać, sekwencyjny wskaźnik liczbowy zaczynając od 0 poświęca się swojej budowniczego funkcja

abuild (5) (id) // => [0,1,2,3,4]

Tym razem zróbmy coś z budowniczym. Wyprostujemy dane wejściowe. Bardzo zaawansowany.

abuild (5) (x=> x * x) 
// => [0,1,4,9,16] 

A może nie zależy nam na danych wejściowych. Zawsze lubię dobry śmiech. Śmieję się z rzeczy ciągle. Można powiedzieć, że I K('ha') & hellip;

abuild (5) (K('ha')) 
// => ['ha','ha','ha','ha','ha'] 

Bum! Bardzo przydatne, prawda? To K


Realizacja

Śmiało i uruchomić go zobaczyć K w akcji!

// id :: a -> a 
 
const id = x=> x 
 

 
// K :: a -> b -> a 
 
const K = x=> y=> x 
 

 
// add :: Number -> Number -> Number 
 
const add = x=> y=> y + x 
 

 
// reduce :: (a -> b) -> b -> [a] -> b 
 
const reduce = f=> y=> ([x,...xs])=> { 
 
    if (x === undefined) 
 
    return y 
 
    else 
 
    return reduce (f) (f (y) (x)) (xs) 
 
} 
 

 
// map :: (a -> b) -> [a] -> [b] 
 
const map = f=> reduce (xs=> x=> [...xs, f(x)]) ([]) 
 

 
// iterate :: Number -> (a -> a) -> a -> [a] 
 
const iterate = n=> f=> x=> 
 
    n > 0 ? [x, ...iterate (n - 1) (f) (f(x))] : [] 
 

 
// abuild :: Number -> (Number -> a) -> [a] 
 
const abuild = n=> f=> 
 
    map (f) (iterate (n) (add (1)) (0)) 
 

 
console.log(abuild (5) (id)) 
 
// => [0,1,2,3,4] 
 

 
console.log(abuild (5) (x=> x * x)) 
 
// => [0,1,4,9,16] 
 

 
console.log(abuild (5) (K('ha'))) 
 
// => ['ha','ha','ha','ha','ha']

+0

Lubię funkcje z humorem: D Czy 'K (" ha ")' śmieje się z kimś lub o kimś ?! – ftor

+1

To tylko mały wgląd we mnie! Lubię rozjaśniać każdą sytuację^_ ^ – naomik