dlaczego nowoczesne programowanie wygląda tak, że jak mamy jakiś struct typu DataFrame pandas
to w tym structcie z jakiegoś powodu jest wbudowana funkcja sort
zamiast mieć w bibliotece pandas funkcję sort która bierze df i warunki sortowania i zwraca posortowany df
nie rozumiem tego tak bardzo
w sensie że zamiast df = df.sort_values(by='x') powinno być df = pd.sort(df, by='x') : z biblioteki pandas bierzemy funkcję sortującą która działa na df z parametrami dalej, zamiast dataframe'u magicznie się sortującego z samego swojego istnienia (?)
lub np. w pythonie jest logiczne list = sorted(list) vs. jakieś dzikie list = list.sort()
na dodatek to jest strasznie nieintuicyjnie gdy próbujemy składać funkcje, np rozważmy coś takiego:
df.sort_values().filter().plot()
trudno mi jest zrozumieć co tu się dokładnie dzieje, porównajmy to z bardziej logicznym, matematycznym wyrażeniem tego:
plot(filter(sort_values))(df) gdzie ciąg przyczynowo-skutkowy jest jasny, widoczny i zgodny z konwencją (funkcje składamy od prawej do lewej)
alternatywnie można by wprowadzić
dla mnie to wygląda tak jakby ktoś się obraził na konwencję składania funkcji i wymyślił całkowicie nowy sposób byle tylko uniknąć składania funkcji xD
ta konwencja prowadzi do tego że jakikolwiek kod w Pythonie jest dla mnie niezrozumiały mimo że ogarniam mniej-więcej co się dzieje w nawet trochę bardziej skomplikowanych programach w C
Chodzi mi głównie o to, że zamiast jasnego podziału na zmienne (które same z siebie się nie zmieniają bez jasnych instrukcji programu) i działające na nich funkcje, robimy miszmasz że wszystko może mieć wbudowane w siebie funkcje.
To tak jakby zamiast 2+2 (co jest uproszczeniem zapisu suma(2,2) gdzie suma to funkcja z R^2 w R dodająca argumenty) pisać 2.dodaj(2)
ps jeśli nie zauważyliście jeszcze to nie jestem programistą, ani nie mówię że potrafię programować - więc oczekuję odpowiedzi na moim poziomie a nie elektrody
#programowanie bardziej #metaprogramowanie
globalbus

@MurrayRothbard brzmi jak bait antyobiektowy.

Tak, można traktować klasy jako struktury z polem this i funkcjami. Tylko takie ich używanie skończy się okropnym kodem.

MurrayRothbard

@globalbus nie wiem co to klasa ani pole this ani dlaczego miałoby się to kończyć okropnym kodem


kazdy tutorial programowania w czymkolwiek innym niż C przedstawia paradygmat klasy samochodu (przypominam że nadal nie wiem co to klasa xD) z wbudowaną funkcją .rusz() czy coś w tym stylu jako prawdę objawioną, nietłumacząc dlaczego nie można tego zrobić w intuicyjny sposób


rzuć linkami

MurrayRothbard

Kolega mi polecił obejrzenie filmiku Haskell in 100 seconds i niesamowicie mi się spodobało. Czemu tak to nie może wyglądać? Z tego co zrozumiałem to w jednej części programu określamy zmienne i funkcje (bez bezpośredniego działania), a potem w mainie wypisujemy po kolei co wykonujemy. Dokładnie tak sobie wyobrażałem programowanie.


Czemu tak nie jest? Czemu tak musi być gorzej?


Tutaj ten filmik -> https://www.youtube.com/watch?v=Qa8IfEeBJqk

Vuaaas

@MurrayRothbard Gdybyś chciał teraz rozszerzyć działanie funkcji sort tworzysz klasę która dziedziczy po klasie z biblioteki, a przez polimorfizm możesz używać raz tego raz tego, Ogólnie temat jest dość długi, najpierw byś musiał ogarnąć programowanie obiektowe (hermetyzacja, dziedzicznie, abstrakcja, polimorfizm, interfejsy), a potem SOLID i wzorce projektowe

globalbus

@MurrayRothbard

Przykłady z obiektami świata rzeczywistego (zwierzątka, samochody itd) są z dupy. Zwłaszcza dziedziczenie, które jest potrzebne początkującemu jak gwóźdź w dupie.


Klasa to lukier składniowy, który ukrywa przed tobą, że to struktura z funkcjami, które operują na tejże strukturze. Elegancko nazywa się to pola i metody.

Cały sens jest taki, żeby zamykać w ramach klasy funkcjonalność.

Np weźmy obrazek bmp. Zamiast trzymać gdzieś w ogólnym pierdolniku tablicę z pikselami, to możemy mieć elegancko opakowany obrazek w obiekcie. Prywatne pola width/height/piksele. Konstruktor, który wczyta nam to z pliku.

Metody, które zwrócą kolor w określonym punkcie (i w ogóle sprawdzą, czy ten punkt istnieje i nie będzie segfault jak spróbujesz to odczytać).


Im masz dłuższy program, tym bardziej potrzebujesz go porządkować. Nawet w C, wrzucenie wszystkiego w jeden plik jest niezbyt mądre, choć się da. Niektórzy w pythonie też tak lubią robić.

MurrayRothbard

@Vuaaas zaznaczyłem że nie potrafię programować ergo niewiele rozumiem, kolega @globalbus dobrze zczaił że znam podstawy C i świetnie tłumaczy


@globalbus jw świetnie tłumaczysz. byłbyś w stanie polecić jakieś linki/książki/wideo które się rozprawiają z tym tematem? jak napisałem, tutoriale dla początkujących przedstawiają to co piszesz jako prawdę objawioną bez tłumaczenia "czemu tak" i gdy je czytam to chcę obciąć jaja autorom za brak elementarnych zdolnosci przewidywania tego, jakie obiekcje wobec tekstu może mieć czytelnik

globalbus

@MurrayRothbard niestety w życiu nie zrobiłem żadnego tutoriala z internetu, ani nie obejrzałem żadnego filmu

Dużo opensource projektów ma "examples", często z testami. Odpalasz i sprawdzasz jak działa.

Łatwiej jest wystartować od cudzego, bo przynajmniej masz na start założoną strukturę, jakąś konwencję nazewniczą itd.


Czytaj cudzy kod i staraj się zrozumieć co on robi. Większość pracy to jest czytanie kodu (twój po jakimś czasie też będzie cudzy). Jak idziesz na juniora, to będziesz miał takie właśnie ambitne zadania typu znalezienie gdzie jest jakaś funkcjonalność, a potem jej zmiana.


Po pierwsze, zainstaluj porządne IDE, które poza podświetlaniem składni będzie w stanie pokazać kontekst (jak klikniesz na typ obiektu, to pokaże ci kod klasy , jakie metody są dostępne itd.). Działający debugger, gdzie ustawisz breakpoint też jest na plus.

MurrayRothbard

@globalbus znalazłem tę stronę


https://maryrosecook.com/blog/post/a-practical-introduction-to-functional-programming


zjedź do przykładu o tytule "Write declaratively, not imperatively"


dokładnie o tym mówię, autor mnie rozumie doskonale, to jest przepiękne


kod pic related to dokładnie to, jak myślę o programowaniu, jak naturalnie przychodzi mi pisanie czegokolwiek!

aeb5b459-280f-48ce-891d-b1c3ea5e1c61
radler

@MurrayRothbard Po prostu świat poszedł w stronę hermetyzacji. W przypadku małych programików nie ma to znaczenia ale jeśli buduje się coś większego to daje to ogromne plusy. W skrócie program nie powinien wiedzieć co się dzieje w środku klasy. To ma plusy, bo jeśli chcemy przetestować fragment kodu to piszemy testy dla konkretnej klasy (bo możemy tak zrobić). Dzięki separowaniu kodu jest to dużo prostsze i zajmuje mniej czasu, a same testy wykonują się szybciej.


  1. Nie jestem pythonowcem i ten przykład z sort jest dla mnie niezrozumiały. W PHP (a dokładniej we frameworku laravel (wiem nie jest to idealny przykład do teoryzowania)) mamy fajną klasę "Collection". Jest dokładnie tym jak się nazywa. Mozemy sobie tworzyć kilka obiektów i wrzucić je do kolekcji. Poniżej pseudokod:

Collection kolekcja = new Collection();

kolekcja.push(new mojObiekt(3));

kolekcja.push(new moj Obiekt(1));

kolekcja.push new mojObiekt(2));


i potem gdy chcemy posortować te elementy to po prostu sortujemy kolekcję tj. kolejność obiektów. Same obiekty się nie zmieniają. Jest separacja między klasą sortującą, a obiektami. I tak to powinno wyglądać.


  1. Jeśli chodzi o składnie

2.add(2)


to ma to jak najbardziej sens. Jeżeli mamy obiekt to jego stan nie powinien być zmieniany przez coś z zewnątrz. Powinna być do tego funkcja pośrednicząca. Dzięki temu nic nie zmieni nam stanu obiektu "przez przypadek" oraz jeśli będziemy chcieli zmienić w przyszłości sposób zmiany tej wartości to zrobimy to w jednym miejsu, a nie 100. Przyczepiłbym się do nazwy tej zmiennej, bo "2" opisuje konkretny stan, a nie to czym dany obiekt jest, powinno być:


liczba.add(2)


  1. Jeśli chodzi o składnie obiekt.cos().cosInnego().itd() to jest to skrót dla:

obiekt = obiekt.cos();

obiekt = obiekt.cosInnego();

obiekt = obiekt.itd();


ponieważ każda z tych metod cos(), cosInnego, itd() na końcu ma coś w stylu "return this", co zwraca zmodyfikowany obiekt. I jak widzisz operacje tutaj czytamy od lewej do prawej więc zapis skrótowy też powinien być czytany w ten sam sposób. Oczywiście to tylko skrót, kwestia składni. Równie dobrze można korzystać z wersji dłuższej, ale więcej kodu to więcej czasu poświęconego aby go wpisać.

globalbus

@radler "na końcu ma coś w stylu "return this", co zwraca zmodyfikowany obiekt."

W większości przypadków tak, ale są też API, które zamiast this zwrócą nowy obiekt tej samej klasy, a poprzedni zostawią niezmieniony. Najśmieszniejsze są przypadki, gdy klasa ma właściwości copy-on-write, czyli kopią staje się dopiero, gdy coś zmodyfikuje obiekt.

@MurrayRothbard z językami czysto funkcyjnymi trzeba uważać, bo czasem to są akademickie zastosowania, że "da się tak napisać program". Funkcyjne programowanie bardzo elegancko wygląda przy współbieżności, nie ma tego wszystkiego czym się męczy studentów (synchronizacja wątków).

Python, języki JVMowe są wieloparadygmatowe, czyli da się fragment napisać tak, a drugi inaczej, w zależności jak jest dla danego celu wygodniej/czytelniej.

radler

@globalbus Zgadza się. Spotkałem się nawet z takimi klasami, które mają 2 różne metody i jedna zwraca klon, a druga referencje do istniejącego obiektu, ale nazwy tych metod w ogóle nie wskazują która co zwraca .

Strus

@MurrayRothbard W Twoim przykładzie zakładasz, że wszystkie funkcje można wywołać na wszystkim, a w prawdziwym zyciu tak to nie działa. Dlatego składnia foo.do_something() jest dużo lepsza, bo:

  1. Wiesz, że dany obiekt rzeczywiście implementuje daną funkcjonalność

  2. IDE może Ci podpowiedzieć, co możesz z danym obiektem zrobić


Języki takie jak Haskell to inna historia, bo tam każdą funkcję pisze się jak najbardziej generycznie - ale już nie wchodząc w szczegóły, to w większości "standardowych" języków się tak nie da.


Co do czytelności - dla większości ludzi po przyzwyczajeniu bardziej czytelne są wywoływania przez kropkę, tym bardziej, że jak masz dużo wywołań z wieloma argumentami, to każdą funkcję możesz dać w osobnej linii. Przy wywołaniach foo(bar(baz())) tak się za bardzo nie da i szybko robi się syf.

Zaloguj się aby komentować