Closure

Odpowiedz Nowy wątek
2019-09-04 14:00
0

Proste rekurencyjne wypisanie kolejnych liczb.

Jestem uczony w stylu starej daty, napisałbym bez wahania w stylu Old.

function logNumbersRecursivelyOld(start, end) {
    console.log(`Recursively looping from ${start} until ${end}`);

    function recurse(left, right) {
        console.log(`Hitting index ${left}`);

        if (left < right) {
            recurse(left + 1, right);
        }
    }

    recurse(start, end);
}

Niedawno usłyszałem, że tak nadmiarowo to się już nie pisze, tylko tak:

function logNumbersRecursivelyNew(start, end) {
    console.log(`Recursively looping from ${start} until ${end}`);

    function recurse(left) {
        console.log(`Hitting index ${left}`);

        if (left < end) {
            recurse(left + 1);
        }
    }

    recurse(start);
}

Wywołanie jawne recurse(left, right) pisze się teraz recurse(left) a aktualna wartość argumentu right idzie w closure?
Jak dla mnie stary (przestarzały?) styl z jawnym przekazaniem obu argumentów function recurse(left, right) jest bardziej czytelny.

@BraVolt: Zmieniłbym tytuł na bardziej opisowy, np. "Czy rekurencja bez domknięć jest przestarzała?". - Silv 2019-09-04 16:08
@Silv A co z Java? ;)))) - BraVolt 2019-09-04 16:12
@BraVolt: nie rozumiem, co masz na myśli? - Silv 2019-09-04 16:14
Nie każdy popularny język ma closures choć wspiera paradygmat funkcyjny - BraVolt 2019-09-04 16:16
Może tak jest, ale wydawało mi się, że pytasz o używanie domknięć, a nie ma sensu o nich mówić, jeśli dany język ich nie wspiera. - Silv 2019-09-04 17:58

Pozostało 580 znaków

2019-09-04 23:53
3

@Silv

Czy rozróżnienie między obiektem a funkcją jest istotne według Ciebie w tym przypadku (Python+średnia)? Według mnie tak, abstrahując od Pythona. Przy obliczaniu średniej obiekt ostatecznie może dla mnie przechowywać tablicę – aczkolwiek mało intuicyjny, również, jest dla mnie obiekt o nazwie Averager. Natomiast funkcja nie powinna jej przechowywać (jako domknięcie).

  The venerable master Qc Na was walking with his student, Anton.  Hoping to
prompt the master into a discussion, Anton said "Master, I have heard that
objects are a very good thing - is this true?"  Qc Na looked pityingly at
his student and replied, "Foolish pupil - objects are merely a poor man's
closures."

  Chastised, Anton took his leave from his master and returned to his cell,
intent on studying closures.  He carefully read the entire "Lambda: The
Ultimate..." series of papers and its cousins, and implemented a small
Scheme interpreter with a closure-based object system.  He learned much, and
looked forward to informing his master of his progress.

  On his next walk with Qc Na, Anton attempted to impress his master by
saying "Master, I have diligently studied the matter, and now understand
that objects are truly a poor man's closures."  Qc Na responded by hitting
Anton with his stick, saying "When will you learn? Closures are a poor man's
object."  At that moment, Anton became enlightened.

:D

Źródło: http://people.csail.mit.edu/g[...]ss-archive-html/msg03277.html

Pozostało 580 znaków

2019-09-05 00:33
cs
0
hauleth napisał(a):

Może czytelny, ale bez domknięcia męczysz stos przekazywaniem w kółko parametru right, który jest stały w każdym wywołaniu.

Tylko jeśli nie ma TCO, co w większości języków już jest. A nawet jak nie ma TCO, to jeśli kompilator/interpreter optymalizuje, to to zostanie wyoptymalizowane, zwłaszcza jeśli IR używa SSA.

Nie znam za dobrze JS, ale chyba tylko Safari ma TCO.
Prosty test po ilu rekurencjach następuje przepełnienie stosu pokazuje, że interpreter JS nie robi żadnej optymalizacji:

var size = 100;
while(true){
  try {
        logNumbersRecursivelyOld(1, size);
        size += 100;
  } catch (e) {
        console.log("Old - stack overflow after " +size + " recursions");
        break;
    }
}
size = 100;
while(true){
    try {
        logNumbersRecursivelyNew(1, size);
        size += 100;
    } catch (e) {
        console.log("New - stack overflow after " +size + " recursions");
        break;
    }
}

Wynik

Old - stack overflow after 14000 recursions
New - stack overflow after 15700 recursions

Pozostało 580 znaków

2019-09-05 00:52
3

@Silv:

Czy rozróżnienie między obiektem a funkcją jest istotne według Ciebie w tym przypadku (Python+średnia)? Według mnie tak, abstrahując od Pythona. Przy obliczaniu średniej obiekt ostatecznie może dla mnie przechowywać tablicę – aczkolwiek mało intuicyjny, również, jest dla mnie obiekt o nazwie Averager. Natomiast funkcja nie powinna jej przechowywać (jako domknięcie).

Tak jak napisał @Maciej Cąderek nie ma większej róźnicy między obiektem a closure. Zauważ jednak, że używając closure, tablica nie może być widoczna z zewnątrz, co ogranicza potencjalne błędy.

Co do kwestii czy ta tablica w ogóle powinna być przechowywana w funkcji, to moim zdaniem w tym przypadku odpowiedź brzmi tak. Wyobraź sobie sytuację w której masz jakąś pętle obliczeń i w każdym kroku musisz policzyć 5 różnych średnich kroczących dla różnych danych. Jeśli wyrzucisz "stan" z funkcji liczącej MA to będziesz go musiał obsłużyć w tej pętli i zaśmiecisz sobie cały kod update'owaniem tablic, a jak będziesz chciał to zrobić wydajniej to musisz trzymać sumę i liczbę elementów, więc na każde obliczenie średniej potrzebujesz 3 linijek kodu zamiast 1, co gorsza w miejscu, gdzie powinieneś mieć logikę biznesową (powiedzmy jakąś formułę matematyczną do obliczania metryki) masz szczegóły implementacji MA. Ten pattern jest na tyle częsty, że nawet w niemutowalnym języku jakim jest Haskell możesz pisać w ten sposób używając StateMonad

Co do pierwotnego pytania, to ja preferuję wersję z closure. Uważam, że używanie redundantnego parametru, dodaje funkcji wewnętrznej niepotrzebnej generyczności i zaciemnia cel, dla którego została napisana.

Pokaż pozostałe 2 komentarze
PS. Ta bardziej abstrakcyjna wywoływałaby tę mniej abstrakcyjną. - Silv 2019-09-05 02:18
PS2. @Kalrais, popraw mnie, jeśli napisałem coś, co wydaje Ci się oczywistą głupotą. - Silv 2019-09-05 02:20
@Silv: Może ten przykład coś Ci da - pastebin . Chcesz wygenerować metryki funkcją f na podstawie na 3 strumieni danych. f wymaga podania średnich kroczących strumieni. Zauważ, że w 2 i 3 implementacji wyciekają Ci szczegóły implementacji średniej kroczącej na poziomie logiki biznesowej. Co liczby linii kodu. Jak masz coś co możesz napisać w x linijkach albo w 3*x linijkach to najprawdopodobniej to co liczysz jest na tyle dobrze określone i złożone, że powinieneś to wyabstrahować. Taka heurystyka ;) - Kalrais 2019-09-05 10:55
@Kalrais: zobaczę dziś wieczorem (skopiowałem sobie). - Silv 2019-09-05 17:58
@Kalrais: przepraszam, ale nie mam czasu wgłębiać się w składnię Pythona, a bez tego nie bardzo rozumiem, do czego zmierzasz. Przyjmijmy, że Ci wierzę, że ten przykład dobrze obrazuje to, o czym mówisz. A co do domknięć, obaj pozostańmy przy swoich zdaniach. Może kiedyś coś więcej się nauczę na ten temat. :) - Silv 2019-09-05 22:59

Pozostało 580 znaków

2019-09-05 09:31
0

Jestem zwolennikiem prostoty i czytelności kodu, co za tym idzie unikania specyficznych rozwiązań. Może to kwestia tego, że czytam kod sprzed 5, 10 lat i mam świadomość, że za następne 5 lat ten kod, poprawiany i modyfikowany, nadal będzie żył.

Pozostało 580 znaków

2019-09-05 12:27
0
Kamil Żabiński napisał(a):

Od jakiegoś czasu czytam i próbuję zrozumieć "Struktura i interpretacja programów komputerowych" z 1996 (Second Edition) i mam wrażenie że ten New Style to tak naprawdę Old Old Style. po prostu wiele języków nie posiadało dobrze działających closure więc trzeba było kantować. W Scheme zawsze były closure więc można było je używać.

I jeszcze odnośnie braku implementacji clusure w Java.

Albo closure albo pure function.
Paradygmat funkcyjny został niejako odkryty na nowo (albo odkurzony) bo fajnie wpasowuje się w programowanie rozproszone, skalowalność itp.

Po wyeliminowaniu closures (i side effects) tak przykrojony język dobrze pasuje do obecnych wymagań.

Pozostało 580 znaków

2019-09-05 12:36
0

"Albo closure albo pure function" - Mógłbyś to rozwinąć, co Masz na myśli?


Pozostało 580 znaków

2019-09-05 12:48
1

https://medium.com/javascript[...]-a-pure-function-d1c076bec976
https://codeburst.io/shared-m[...]handle-with-care-351b8fa2f939

Góogle: Closure vs Pure Functions i dyskusje

Java jest mocno współbieżna, tak bym to określił, Pure JS raczej single threaded. Oracle świadomie podjęło decyzję o nieimplementowaniiu closures, w JS w niczym one nie przeszkadzają a przy tym czynią z JS język 'prawdziwie funkcyjny' (święta wojna czy Java jest językiem funkcyjnym czy nie i dlaczego JS pokazuje wyższość nad Javą. Zażartość dyskusji może miejscami tylko niewiele odbiegać od dyskusji Android vs. iOS).

Moje zdanie jest takie, że język - narzędzie dobieramy do zadania, samym narzędziem posługujemy się bez hardcoru, żeby w przyszłości w miarę prosto i bezpiecznie można było zmienić narzędzie bez przewracania kodu do góry nogami w celu wyeliminowania kruczków konkretnego języka - narzędzia.

Na jednej z prelekcji na temat programowania funkcyjnego. jak pamiętam w Krakowie, Venkat Subramaniam ładnie ujął temat, mniej więcej tak
sharing is good
mutability is good
sharing mutability is evil

edytowany 1x, ostatnio: BraVolt, 2019-09-05 12:52

Pozostało 580 znaków

2019-09-05 13:11
0

W Javie od wersji 8 masz closures, wcześniej ich nie było, bo nie było funkcji anonimowych, zresztą jak już było napisane closure i obiekty to właściwie to samo, więc można powiedzieć że Java od zawsze miała closures.

Pure functions i closure to są różne rzeczy, chyba każdy język funkcyjny ma domknięcia, to jest semantyka przeniesiona wprost z rachunku lambda. W Haskellu bez domknięć ciężko coś napisać, a to przecież czysty język (poza IO).

Java 8 nie implementuje natywnie closures - BraVolt 2019-09-05 13:28
Nie wiem co masz na myśli mówiąc natywnie. Jeśli chodzi Ci o to, że przekazywane do lambdy zmienne muszą być final to moim zdaniem jest to szczegół implementacji. W Haskellu też muszą być final bo tam wszystko jest niemutowalne. ;) - Kalrais 2019-09-05 13:45

Pozostało 580 znaków

2019-09-05 13:23
0

@BraVolt: Po pierwsze, źle to odczytałem - widziałem tam closures vs functions as first class citizens:). Ale do rzeczy, ano nie, nie ma dyskusji, powyżej jest o rachunku lambda. Jeżeli jesteś w języku funkcyjnym, to domknięcie jest nad wartością immutable, więc czystość funkcji nie jest złamana; w przeciwieństwie do powyższego przykładu z Pythona, gdzie domykamy nad mutable listą i jeszcze zmieniamy ją w funkcji.


Pozostało 580 znaków

2019-09-05 13:27
0

Z tą czystością jest jak z tail recursion w JVMkach. Niby miała być, niby są języki na JVM gdzie to hula znakomicie, a w większości przypadków nie ma.
W JS pure function nie musimy się za bardzo przejmować, co innego w Java.

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0