• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Przykładowy prosty problem. Jak go dobrze rozwiązać?

Object Storage Arubacloud
0 głosów
451 wizyt
pytanie zadane 16 marca 2022 w C i C++ przez Daaa22 Dyskutant (8,250 p.)

Przykładowy kod

int tab[n];

//kod ktory odpowiada za umieszczenie czegos w tab

for(int i = 0; i < n; i++)
{
for(int j = i; j < n; j++)
{
//jakis kod ktory sprawdza czy dla danego tab[i] spelniony jest jakis warunek dla kazdego tab[j], nazwijamy to "warunek"
}
//kod do wykonania jeżeli "warunek" zostanie spełniony, jezeli nie zostanie spelniony to nie wykonuj tego kodu
}

Najprostsze rozwiazanie jakie mi do glowy przychodzi to

int tab[n];
bool warunek;

//kod ktory odpowiada za umieszczenie czegos w tab

for(int i = 0; i < n; i++)
{
warunek = false;
for(int j = i; j < n; j++)
{
if(/*jakis warunek zalezny od tab[i] i tab[j]*/)
{
warunek = true;
break;
}
}
if(warunek)
{
//kod do wykonania
}
}

ale brzydko wygląda deklarowanie nowej zmiennej. Tak to powinno być czy jakiś lepszy wzór na to jest?

2
komentarz 16 marca 2022 przez j23 Mędrzec (194,920 p.)

(...) ale brzydko wygląda deklarowanie nowej zmiennej.

Brzydko wygląda kod bez wcięć. A jeśli kod wymaga dodatkowej zmiennej, by robił to, co powinien, to nie widzę problemu.

2
komentarz 16 marca 2022 przez VBService Ekspert (253,300 p.)

W Twoim przykładzie zmienna warunek jest w pewnym sensie tzw. flagą, stosuje się taki sposób programowania.  laugh, więc chyba nie ma mowy o ...

... ale brzydko wygląda deklarowanie nowej zmiennej

komentarz 17 marca 2022 przez Wiciorny Ekspert (269,790 p.)
flagi to taki dirty code,  pozbywamy sie flaagi! to jest główne motto.
Zamiast flagi : tworzymy czytelne api i tyle ( coś na bazie wrapper-fasada)
1
komentarz 17 marca 2022 przez VBService Ekspert (253,300 p.)
edycja 18 marca 2022 przez VBService

No, w sumie można to zapisać np. tak

int tab[n];
 
//kod ktory odpowiada za umieszczenie czegos w tab
 
for (int i = 0; i < n; i++)
{
  for (int j = i; j < n; j++)
  {
    if (/*jakis warunek zalezny od tab[i] i tab[j]*/)
    {
      //kod do wykonania
      break;
    }
  } 
}

 

ale wink

In the C++ standard library you can see flags as part of a stream interface (std::ios_base::flags - cppreference.com) representing certain state of an input or output. or as part of a regex interface (std::regex_constants::syntax_option_type - cppreference.com) to specify certain options for the regular expression parser.

komentarz 20 marca 2022 przez Daaa22 Dyskutant (8,250 p.)

A przykładowo taki kod:

int tab[ i ];

bool flag = false;
for(int i = 0; i < n - 1; i++)
{
 for(int j = i; j < n; j++)
 {
  if(/*jakis warunek zalezny od tab[i] i tab[j]*/)
  {
   flag = true;
   break;
  }
 }
 if(flag)
 break;
}

Da się jakoś ładnie to zrobić bez flagi? Może goto użyć? Jak będzie najwydajniej?

1
komentarz 20 marca 2022 przez Wiciorny Ekspert (269,790 p.)

dobrze, ale to że można czegoś używac @VBService to nie znaczy, że coś nie jest dobrą praktyką. Wzorce projektowe wyraźnie mówią o tym, że w programach flaga nie jest porządana, szczególnie jako parametr funkcji. 

In the C++ standard library you can see flags as part of a stream interface

to że coś jest częścią biblioteki, implementacji nie oznacza- że to jest dobrze napisane. Java, C# mają mnóstwo Bugów i błędnych implementacji, w większości przypadku dlatego że mają one być niezawodne- w swoim działaniu, a nie "czyste" w zakresie POPRAWNEJ ARCHITEKTÓRY i clean code.  

Flaga to po prostu jednobitowa zmienna, więc generalnie o to chodzi, żę nie jest to dobre i lepiej np w takiej instrukcji warunkowej użyć "obiektu" czegoś co WIĘCEJ POWIE PROGRAMIŚCIE o problemie :) 

Flags are problematic because they can have different purpose based on the context. A similar function called with a flag may mislead you into thinking that it has the same meaning. But another developer may default their calculations to miles and expect the caller to pass false when they want to use kilometers.

Mają względnie niewielki kontekst, są LIMITERAMI,  ograniczają czytelność aplikacji 
sam Martin Fowler o tym pisze https://martinfowler.com/bliki/FlagArgument.html

komentarz 20 marca 2022 przez Wiciorny Ekspert (269,790 p.)
edycja 20 marca 2022 przez Wiciorny

@Daaa22,  "najwydajniej" tu nie chodzi o wydajność, a czytelność i niezawodność, w obu przypadkach wydajność jest taka sama. Więc nie myl pojęć bo tutaj nie poprawiasz wydajności.

Odp. na pytanie jest zależna od tego : jaką role w bierzącym programie pełni flaga

bool flag = false;
for(int i = 0; i < n - 1; i++)
{
 for(int j = i; j < n; j++)
 {
  if(/*jakis warunek zalezny od tab[i] i tab[j]*/)
  {
   flag = true;
   break;
  }
 }
 if(flag)
 break;
}

w tym programie flaga jest totalnie niepotrzebna. . 

Też to nie jest tak zero-jedynkowe, że flaga jest zawsze pase. Ale często flaga jest niepotrzebna i znacznie lepiej można ją zastąpić czytelną funkcją :) 

komentarz 23 marca 2022 przez Daaa22 Dyskutant (8,250 p.)

@Wiciorny

Skoro jest niepotrzebna to jak byś to rozwiązał? Załóżmy że mamy 2 pętle jedna w drugiej w taki sposób że nie da się zrobić z niej 1 pętli. Musi być jedna w drugiej. Jak wtedy przerobiłbyś mój kod żeby był dobry?

komentarz 24 marca 2022 przez adrian17 Ekspert (344,860 p.)
Mam wrażenie wiciorny wybrał dziwny moment na rant nie na temat (bo ktoś użył przy nim słowo "flaga") i tyle ;)
komentarz 24 marca 2022 przez tangarr Mędrzec (154,860 p.)

Jeżeli sprawdzenie warunku staje się złożone (czytaj mało czytelne) to możesz je wyeksportować do osobnej funkcji (lub lambdy)

auto test = [&tab, &n]() {
    for(int i = 0; i < n - 1; i++)
    {
        for(int j = i; j < n; j++)
        {
            if(/*jakis warunek zalezny od tab[i] i tab[j]*/)
                return true;
        }
    }
    return false;
}

if (test()) {
    //kod do wykonania
}

 

3 odpowiedzi

0 głosów
odpowiedź 24 marca 2022 przez mokrowski Mędrzec (155,460 p.)

Takie elementy jak flagi czy "surowe czytania po pamięci", generalnie mają sens tylko blisko sprzętu albo w trakcie głębokiej optymalizacji. Flagi np. występują wtedy bardzo często jako znacznik w danym rejestrze/porcie i ... tak jest :)

Co do goto, jest kilka przypadków gdy broni się w C (bez plusów). Co do C++, goto jest zbędne w codziennym stosowaniu.

Nie bardzo rozumiem i nie wiem czy nie przekombinowałeś. Jeśli chodzi o sekwencyjne przeglądanie a nie jakieś kombinacje typu złączenie kartezjańskie czy połączenie par, to przecież taki kod załatwia sprawę. Coś więcej można powiedzieć jeśli zna się dokładniej domenę problemu. Bez gwarancji że trafiam, ale zerknij do algorytmów. Szczególnie std::accumulate, std::transform, std::generate, std::all_of, std::any_of ...

#include <iostream>    
    
template<typename Condition, typename Effect>    
void run_action(int * tab, size_t size, Condition condition, Effect effect) {    
        for (auto i = 0U; i < 10; ++i) {    
                for (auto j = i + 1; j < 10; ++j) {    
                        if (condition(tab[i], tab[j])) {    
                                effect(tab[i], tab[j]);    
                        }    
                }    
        }    
    
}    
    
bool condition(int a, int b) {    
        return !(b % a);    
}    
    
void effect(int a, int b) {    
        std::cout << b << " divided by " << a << '\n';    
}    
    
int main() {    
        int tab[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};    
        run_action(tab, 10, condition, effect);    
}

 

komentarz 25 marca 2022 przez j23 Mędrzec (194,920 p.)

W run_action nie używasz size.

0 głosów
odpowiedź 24 marca 2022 przez adrian17 Ekspert (344,860 p.)

//jakis kod ktory sprawdza czy dla danego tab[i] spelniony jest jakis warunek dla kazdego tab[j], nazwijamy to "warunek"

"jakiś warunek dla każdego" to podręcznikowy std::all_of.

Więc tak na oko, jeśli dobrze zrozumiałem pytanie:

for (auto it = tab, end=tab+n; it<end; it++) {
    bool all = std::all_of(it, end, [](int x){ return x % 2; }); // warunek tutaj
    if (all) {
        // kod do wykonania jeżeli "warunek" zostanie spełniony dla kazdego
        // miedzy obecnym a ostatnim
    }
}

 

0 głosów
odpowiedź 24 marca 2022 przez rafal.budzis Szeryf (85,260 p.)

Na C++ się nie znam ale takie rzeczy można ukryć za pomocą iteratora. Możesz zrobić własny iterator który zawsze będzie iterował po 2 elementach na raz. Dzięki czemu pozbędziesz się 2 pętli (a bardziej ukryjesz je w domenie iteratora)

https://refactoring.guru/pl/design-patterns/iterator

Czy wydajnościowo to najlepsze rozwiązanie : Raczej nie
Czy będzie ono czytelniejsze : TAK
Czy domena biznesowa (warunki) będą łatwiejsze do utrzymania: TAK

Nie zawsze najwydajniej znaczy najlepiej ;)
 

 

komentarz 24 marca 2022 przez adrian17 Ekspert (344,860 p.)
Raczej tutaj nie chodzi o "dwa elementy na raz".

A co do iteratorów, to w C++ie one są jednym z podstawowych konceptów języka - w szczególności, wskaźnik jest iteratorem :) Nikt nie myśli o tym w kategoriach "design patterns", to nie Java.
komentarz 24 marca 2022 przez rafal.budzis Szeryf (85,260 p.)

No jak mamy dwie pętle z tą sama tablicą i chcemy sprawdzić każdy z każdym. Więc będą to pary 2 elementów. Więc iterator mógłby zwracać dwie referencje przy każdym odczycie. W C++ nie wiem jak to dokładnie zapisać więc uznajmy to za pseudo kod ;) 

 

int i = 0;
int j = i;

next() {
    i++;
    if(i < n){
        return [tab[i], tab[j]];
    }
    i = 0;
    j++;
    if(j < n) {
        return [tab[i], tab[j]];
    }
    return null;
}

while(temp = next()){
    if(temp[0].compare(temp[1]) {
        break;
    }
}



 

komentarz 24 marca 2022 przez adrian17 Ekspert (344,860 p.)
Tylko w oryginalnym pytaniu logika była "w pętli po i: czy dla danego tab[i], warunek jest spełniony dla każdego tab[j] gdzie i<=j<n"; więc logika jest powtarzana dla każdego i, a nie raz na cały zbiór (i,j).
1
komentarz 25 marca 2022 przez rafal.budzis Szeryf (85,260 p.)

Nie rozumiem o co chodzi. Przecież teraz też podbijam i a potem dopiero j. W pętli while możesz sprawdzać tylko pierwszy element. Nie musisz sprawdzać drugiego. Nasz prymitywny iterator możesz też zwracać same indeksy zamiast wartości / referencji. 

Przykładowy kod działający z JavaScript:

var tab = [1,2,3,4,5,6];
var n = tab.length;
var i1 = 0;
var j1 = 0;
 
function next() {
    i1++;
    if(i1 < n){
        return [i1, j1];
    }
    i1 = 0;
    j1++;
    if(j1 < n) {
        return [i1, j1];
    }
    return null;
}

while(temp = next()){
  const [i, j] = temp;
  if (tab[i] === null ){
    // logika dla pierwszego elementu
  }
  if(temp[i] === temp[j]) {
    // logika dla 2 elementów
      break;
  }
}



 

Podobne pytania

+1 głos
3 odpowiedzi 235 wizyt
pytanie zadane 9 grudnia 2020 w C i C++ przez malvator Użytkownik (720 p.)
+1 głos
1 odpowiedź 195 wizyt
pytanie zadane 10 października 2018 w Nasze projekty przez kevin Mądrala (5,010 p.)
+1 głos
1 odpowiedź 693 wizyt
pytanie zadane 23 września 2018 w C i C++ przez stones321 Nowicjusz (200 p.)

92,568 zapytań

141,420 odpowiedzi

319,624 komentarzy

61,956 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...