Wątek przeniesiony 2023-04-08 18:13 z C/C++ przez Althorion.

Czy używanie krotek jest w poprzek obiektowosci trochę?

0

W temacie wątku pozwoliłem sobie na drobną parafraze tego komentarza napisanego przez @opiszon

W wątku zaproponowano rozwiązanie za pomocą std::tuple

czy rzeczywiście konstrukcja jest passé ?

4

Dla mnie użycie krótki to tak jakbym użył listy albo tablicy generycznych obiektów do zwrócenia danych z metody.
Nie niesie za sobą żadnej meta informacji co zwracamy.

W tamtym poście zwracane są współrzędne więc nie są to 2 niezwiązane ze sobą wartości tylko dwa parametry de facto 1 obiektu więc dla mnie sensowne jest obudowanie konkretnym typem a nie używanie krotki.

5

Nie jestem może najlepszą osobą do udzielania odpowiedzi na to pytanie, bo mam raczej negatywną opinię o paradygmacie obiektowym, więc wypowiem się może nieco… ortogonalnie.

Problemem ze zwracaniem przez krotkę jest to, że krotka jest bardzo ogólna i ma nader luźną strukturę. Możesz mieć mnóstwo funkcji zwracających std::tuple<double, double> (jak w tamtym temacie), gdzie jednocześnie ich rezultaty nie są w ogóle ze sobą kompatybilne. Jedna zwraca długość i szerokość geograficzną, druga wzrost i wagę, trzecia temperaturę i czas…

Osobiście wolę mieć dużo silniejsze typy.

1

Po zastanowieniu to jednak sie zgodzę że to może nieść sporo potencjalnych problemów, czyli to co napisali @Althorion @opiszon że ten sam typ danych to nie koniecznie to samo
ale też potencjamne niebezpieczenstwo przy zmianach np. w poniższym kodzie (zrodło) zmieniamy typ z double na single to wyklada sie std::get<double>(student1)
zmieniamy kolejnosc to mamy potencjalny problem w std::get<1>(student0)
itd itd.

w przypadku zwrocenia struct/class troche to bedzie trudniej uszkodzić

#include <tuple>
#include <iostream>
#include <string>
#include <stdexcept>
 
std::tuple<double, char, std::string> get_student(int id)
{
    switch (id)
    {
        case 0: return {3.8, 'A', "Lisa Simpson"};
        case 1: return {2.9, 'C', "Milhouse Van Houten"};
        case 2: return {1.7, 'D', "Ralph Wiggum"};
        case 3: return {0.6, 'F', "Bart Simpson"};
    }
 
    throw std::invalid_argument("id");
}
 
int main()
{
    const auto student0 = get_student(0);
    std::cout << "ID: 0, "
              << "GPA: " << std::get<0>(student0) << ", "
              << "grade: " << std::get<1>(student0) << ", "
              << "name: " << std::get<2>(student0) << '\n';
 
    const auto student1 = get_student(1);
    std::cout << "ID: 1, "
              << "GPA: " << std::get<double>(student1) << ", "
              << "grade: " << std::get<char>(student1) << ", "
              << "name: " << std::get<std::string>(student1) << '\n';
 
    double gpa2;
    char grade2;
    std::string name2;
    std::tie(gpa2, grade2, name2) = get_student(2);
    std::cout << "ID: 2, "
              << "GPA: " << gpa2 << ", "
              << "grade: " << grade2 << ", "
              << "name: " << name2 << '\n';
 
    // C++17 structured binding:
    const auto [ gpa3, grade3, name3 ] = get_student(3);
    std::cout << "ID: 3, "
              << "GPA: " << gpa3 << ", "
              << "grade: " << grade3 << ", "
              << "name: " << name3 << '\n';
}
3

Używanie krotek zamiast wyspecjalizowanych typów nie ma za bardzo sensu, jeśli w języku mamy wsparcie dla rekordów (łatwego tworzenia, dekonstrukcji etc.) dlatego np. w Purescript postanowili nie dorzucać cukru syntaktycznego dla krotek, po to, żeby nie zachęcać do ich używania https://github.com/purescript/documentation/blob/master/language/Differences-from-Haskell.md#tuples

2

Jeśli w naszych obiektach możemy używać String, Integer, List (nie ważne czy to będą prymitywy jak w Javie czy obiekty jak w rubym), to tak samo możemy użyć tuple.

Jeśli w imię obiektowości chcesz usuwać tuple, to musisz też usunąć inty, stringi i listy.

2

Tam gdzie krotka ma sens domenowy - nie ma problemu. Pewnie jakieś sytuacje gdy musimy zwrócić z metody niepowiązane ze soba dane dla których nie ma sensu opakowanie ich w strukturę.

Tylko większość przykładów użycia krotek, które widziałem w życiu, wyglądały tak jak w zalinkowanym poście - mamy do zwrócenia współrzędne składające się z parametrów typu double? Zwróćmy więc krotkę (albo tablicę doubli) zamiast obiektu opisującego współrzędne...

Spoko. Ale wolę jednak silne typowanie i jego zalety.

4

Dla mnie użycie tupli ma sens wyłącznie w kodzie szablonowym, kiedy siłą rzeczy nie da się użyć nazwanych struktur. W przeciwnym wypadku: użyj stuktury, jej zdefiniowanie to nie jest rocket science.

2

Dopuszczałbym krotki w kodzie, gdzie nie da się "nadać nazwy", bo pełna rzeczywistość jest nieznana w czasie kompilacji, tak jak używamy map jako dynamicznego kontenera na dane, z którymi się spotkamy dopiero na wykonaniu.

W podanym kontekście to nie zachodzi.

opiszon napisał(a):

Tam gdzie krotka ma sens domenowy - nie ma problemu.

Właśnie, o to chodzi. Jakiś istnieje, ale wiesz, że trudno mi podać od ręki taki kontekst akurat na dwie (stałą ilość) zmienne? Stała ilość (np dwie) z dużą siłą nasuwa ich rolę w przetwarzaniu, więc nazwę.
Na map o wiele łatwiej, choć "wszystkie pola z kwerendy SQL", "wszystkie pola z CSV" itd... .

opiszon napisał(a):

Ale wolę jednak silne typowanie i jego zalety.

+1

2

By odpowiedzieć na pytanie OPa musiano by w końcu ustalić jednoznacznie czym owa mityczna Obiektowość jest. Jak dotąd ludzkość nie doświadczyła tego typu definicji, mamy za to wiele radosnych prób zaliczania do niej właściwie większej ilości rzeczy niż niezaliczenia.

0

Na OOP się nie znam, ale w ostatnim czasie spotkałem wielkie krotki (bo tak komuś było prościej tworzyć zapytania do bazy w Scali za pomocą Slicka) i ogólnie stwierdzam że jest to straszne :D Dlatego czy OOP czy FP krotki bym ograniczał, najlepiej do dwuelementowych i to jeszcze że prawa wartośc jest innego typu niż lewa. Miejsca gdzie krotki (pary) się dobrze sprawdzają to zamiana mapy na listę par klucz wartość, lub jakieś proste przemapowania gdzie za dwie linijki tej krotki się pozbędziemi.

Jak zwracamy krotkę z funkcji/metody to jest to ogólnie masakra. Nawet jak nie mamy pomysłu na nazwę struktury zwracanej z funkcji someFunction to lepiej ją nazwać SomeFunctionResult i móc ponazywać pola niż zwracać krotkę 4 elementową bez nazwanych pól

1

Bo to C++, weź język gdzie krotka może mieć ponazywane pola. No ale to język dla osób ceniących komfort ;)

0

W takim c# (też swift, python, typescript i pewnie wielu innych) możesz sobie nadać nazwy składowym i wtedy nie ma prawie żadnej różnicy między takim tuple a stworzeniem osobnej struktury. Jak mam prywatną metodę której używam w jednym miejscu i potrzebuję po prostu zwrócić więcej niż jedną rzecz to zazwyczaj używam tupla z lenistwa bo mniej pisania i mniej kodu. Kiedyś też raczej się używało do tego parametrów wyjściowych częściej niż deklarowało osobne struktury do wszystkiego.
Nawet jednolinijkowe rekordy potrafią zrobić bałagan - nawet nie tyle w kodzie co w przestrzeni nazw, czasem po prostu nie ma potrzeby nazywania tego co zwracamy bo od razu sobie to destrukturyzujemy do osobnych zmiennych.
Po co mi jakieś "SomeFunctionResult" którego potrzebowałem w jednym miejscu w podpowiadaniu składni.

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