Autor Wątek: STL, usuwanie bieżącego elementu w pętli  (Przeczytany 5260 razy)

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Październik 22, 2009, 17:23:23
To jest lenistwo innego poziomu. Ja wiem co robią stable_partition() i remove(). Pisząc ręcznie musiałbym pamiętać jak to robią. ;D
To pokaż mi swoje lenistwo, w przypadku gdy zamiast "if(should_be_deleted(vec[j]))" będzie po prostu "if(vec[j].is_killed)". ;)
« Ostatnia zmiana: Październik 22, 2009, 17:28:52 wysłana przez Krzysiek K. »

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline naleth

  • Użytkownik

# Październik 22, 2009, 18:26:15
(...) w przypadku gdy zamiast "if(should_be_deleted(vec[j]))" będzie po prostu "if(vec[j].is_killed)". ;)

Wtedy będzie coś takiego:
v.erase(std::remove_if(v.begin(), v.end(), std::mem_fun_ref(&c::is_killed)), v.end());

Offline yarpen

  • Użytkownik

# Październik 22, 2009, 19:20:35
(...) w przypadku gdy zamiast "if(should_be_deleted(vec[j]))" będzie po prostu "if(vec[j].is_killed)". ;)

Wtedy będzie coś takiego:
v.erase(std::remove_if(v.begin(), v.end(), std::mem_fun_ref(&c::is_killed)), v.end());
Que? Przeciez is_killed to zmienna, a nie f-kcja.

Offline Reg

  • Administrator
    • Adam Sawicki - Home Page

# Październik 22, 2009, 19:34:42
Ja bym chętnie używał algorytmów, bo popieram podejście żeby nie pisać pętli i nie pamiętać "jak" to się robi, ale tylko gdybym nie musiał jako treść warunku pisać funktora albo funkcji. Cóż, czekamy na C++0x...

Offline Dab

  • Redaktor
    • blog

# Październik 22, 2009, 19:37:08
Problem w tym że kod upstrzony takim STL-owym badziewiem jest zupełnie nieczytelny dla kogoś poza autorem (nie wiadomo w sumie ani *co* robi, ani *jak* robi).

Offline naleth

  • Użytkownik

# Październik 22, 2009, 21:41:39
(...) w przypadku gdy zamiast "if(should_be_deleted(vec[j]))" będzie po prostu "if(vec[j].is_killed)". ;)

Wtedy będzie coś takiego:
v.erase(std::remove_if(v.begin(), v.end(), std::mem_fun_ref(&c::is_killed)), v.end());
Que? Przeciez is_killed to zmienna, a nie f-kcja.

No proszę, ten szczegół mi umkną :). Oczywiście ten kod zadziała tylko, gdy is_killed jest funkcją składową klasy. Rzeczywiście nie wiem, jak to zrobić ładnie w przypadku, gdy jest to zmienna, więc przepraszam za zamieszanie.

Tego typu idiomy, jak erase-remove są moim zdaniem mimo wszystko całkiem przydatne i wygodne - jeśli zarówno osoba pisząca jak i czytająca wiedzą o istnieniu takiego idiomu, to wszystko jest prościej zrozumieć. Podstawową wadą jest jednak to, iż czasami jest to mało elastyczne, czego najprostszy przykład widać powyżej. O ile w tym przypadku można byłoby to pewnie jakoś rozwiązać (np. poprzez boost:lambda), to najczęściej warunek jest nieco bardziej skomplikowany i "protezy" w stylu boost::lambda są strasznie niewygodne, choćby ze względu na problemy z debugowaniem. Jedyna nadzieja w nadchodzącym c++0x. Jeśli lambda wyrażenia będą działać odpowiednio szybko a ich pisanie będzie odpowiednio wygodne, to wtedy takie idiomy mają szanse stać się wygodniejszym środkiem IMHO.

Offline Aithne

  • Użytkownik

# Październik 23, 2009, 09:57:04
Można dać sobie spokój z dziwactwami C++ i użyć języka, w którym to się robi łatwo i przyjemnie. Zgadnijcie, jaki język mam na myśli i czemu właśnie D.

Offline yarpen

  • Użytkownik

# Październik 23, 2009, 10:34:27
Można dać sobie spokój z dziwactwami C++ i użyć języka, w którym to się robi łatwo i przyjemnie. Zgadnijcie, jaki język mam na myśli i czemu właśnie D.
A pozniej pognac na kucyku w strone zachodzacego slonca. Dzialamy w systemie z pewnymi ograniczeniami, nie mozemy sobie wybierac jezykow wg widzimisie i przyjemnosci.
Generalnie jednak problem jest wyolbrzymiany, dodatkowa linijka nikogo nie zabije, a taki fragment kodu pisze sie 1-2 razy i tyle.
erase-remove sie strasznie slabo skaluje.

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Październik 23, 2009, 11:28:13
Cytuj
Jedyna nadzieja w nadchodzącym c++0x.
A o starych dobrych makrach to już się zapomniało? Przy okazji można to zrobić dużo ładniej niż zagnieżdżając całą garść STLowych wywołań.

Offline Aithne

  • Użytkownik

# Październik 23, 2009, 11:39:07
Ale makra są złe, to one rozpętały drugą wojnę światową, jak możesz sugerować ich użycie? ;P

Offline ConayR

  • Użytkownik

# Październik 23, 2009, 11:51:07
Problem w tym że kod upstrzony takim STL-owym badziewiem jest zupełnie nieczytelny dla kogoś poza autorem (nie wiadomo w sumie ani *co* robi, ani *jak* robi).
Przecież oszczędzasz 3 linijki kodu. W to miejsce dajesz 3 linijki komentarzy na temat tego co i jak się dzieje i szafa gra! ;)

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Październik 23, 2009, 13:04:16
Ale makra są złe, to one rozpętały drugą wojnę światową, jak możesz sugerować ich użycie? ;P
Makra są złe. Tak samo jak młotki. Każdy wie, że młotki są złe. Można się w palec uderzyć. Więc młotki są złe. Nie wolno sugerować używania młotków.


(a tak gwoli ścisłości, to młotki rozpętały druga wojnę światową) ;)

Offline bies

  • Użytkownik

# Październik 23, 2009, 13:33:21
To jest lenistwo innego poziomu. Ja wiem co robią stable_partition() i remove(). Pisząc ręcznie musiałbym pamiętać jak to robią. ;D
To pokaż mi swoje lenistwo, w przypadku gdy zamiast "if(should_be_deleted(vec[j]))" będzie po prostu "if(vec[j].is_killed)". ;)
Primo: o ile jeszcze czytanie przez wszystkich składowej is_killed wydaję się być ok to pisanie przez wszystkich do tej składowej jest podejrzane, ale nieważne. Secundo: proszę bardzo:
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/bind/bind.hpp>

using namespace std;
using namespace boost;

struct A {
    bool is_killed;
    A() : is_killed(false) {}
};

int main() {
    vector<A> v;
    for (unsigned i = 0; i < 5; ++i)
        v.push_back(A());
    cout << v.size() << endl;

    v[2].is_killed = true;
    v[3].is_killed = true;

    v.erase(remove_if(v.begin(), v.end(), bind(&A::is_killed, _1)), v.end());
    cout << v.size() << endl;
}
Z tym, że Boost.Lambda (w powyższym przypadku wystarczył Boost.Bind, ale nierzadko będzie potrzebna Boost.Lambda) będę prawdopodobnie używał jeszcze jakieś 2 miesiące. Później będzie gcc 4.5 i lambdy C++0x. I wbrew temu co pisze Naleth w powyższym przypadku problemu z debugowaniem nie będzie (bo tu nie ma co debugować).

// edit
W gruncie rzeczy to częściej używam Boost.Lambda/Boost.Bind niż STL-owe not1, ptr_fun, mem_ptr_fun.

Yarpen: co to znaczy, że się słabo skaluje?
« Ostatnia zmiana: Październik 23, 2009, 13:44:05 wysłana przez bies »

Offline Dab

  • Redaktor
    • blog

# Październik 23, 2009, 14:58:37
I wciągamy dziesiątki kilobajtów boosta, żeby zrobić coś co w czystym C++ zajmuje linijkę? :)
A bez boosta warunek w rodzaju (x > 6 && x <= 9) wygląda jak zło? :)

Nie no, lambda ma sens w pewnych przypadkach ale tylko jeżeli jest częścią języka. W takim C# np. możemy sobie po prostu napisać mylist.RemoveAll(p => p.Key > 6 && p.Key <= 9) i jasno widać *co* się dzieje. A że nie operuję w C# na kolekcjach mających miliony elementów (i nie mam magicznego limitu 16 ms na klatkę) to nie mam rozterek na temat tego *jak* to się dzieje.

Cytuj
Przecież oszczędzasz 3 linijki kodu. W to miejsce dajesz 3 linijki komentarzy na temat tego co i jak się dzieje i szafa gra!
O to, to. ;)


Offline bies

  • Użytkownik

# Październik 23, 2009, 15:12:55
I wciągamy dziesiątki kilobajtów boosta, żeby zrobić coś co w czystym C++ zajmuje linijkę? :)
Nie, ja wciągam. KrzysieK się pytał o moje lenistwo. Co Wy robicie to już Wasza sprawa. Nie namawiam.

// edit
A w ogóle w powyższym przypadku Boost jest zbędne. Wystarczy std::tr1::bind():
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;
using namespace std::placeholders;

struct A {
    bool is_killed;
    A() : is_killed(false) {}
};

int main() {
    vector<A> v;
    for (unsigned i = 0; i < 5; ++i)
        v.push_back(A());
    cout << v.size() << endl;

    v[2].is_killed = true;
    v[3].is_killed = true;

    v.erase(remove_if(v.begin(), v.end(), bind(&A::is_killed, _1)), v.end());
    cout << v.size() << endl;
}

// edit^2
Tak mi przyszło do głowy, że za często odruchowo włączam <boost/*> zamiast sprawdzić, czy nie ma czegoś w TR1. Tam jest naprawdę sporo fajnych rzeczy przeniesionych z Boosta: [1].

[1] Wikipedia: C++ Technical Report 1
« Ostatnia zmiana: Październik 23, 2009, 15:44:30 wysłana przez bies »