Jak drukować wszystkie elementy tablicy oddzielone jakimś znakiem?

0

Chcę mieć wydrukowane (przykład roboczy):

a+b+c+k+m+n+u+v+w+x+y+z <= 100

Najchętniej bym napisał:

        vector<char> zmienne {'a', 'b', 'c', 'k', 'm', 'n', 'u', 'v', 'w', 'x', 'y', 'z'};
	
	for(char zmienna: zmienne)
		cout << zmienna << "+";
	cout << "\b <= 100\n";

Ale to działa tylko, jeśli drukuję na terminal. Przy przekierowaniu outputu do pliku \b już nie ma semantyki usunięcia ostatnio wydrukowanego znaku, ku mojej irytacji.

Najlepsze, co znam:

	vector<char> zmienne {'a', 'b', 'c', 'k', 'm', 'n', 'u', 'v', 'w', 'x', 'y', 'z'};
 
	for(auto zmienna_indeks = 0; zmienna_indeks < zmienne.size(); zmienna_indeks++) {
		char zmienna = zmienne[zmienna_indeks];
		cout << zmienna;
		if(zmienna_indeks < zmienne.size()-1)
			cout << "+";
	}
	cout << " <= 100\n";

Działa. Ale wydaje mi się to istotnie brzydsze. Na pewno dłuższe o 3 linijki.

Stąd pytanie, czy jest jakiś sensowniejszy / ładniejszy / bardziej idiomatyczny sposób drukowania takich rzeczy?

5
vector<char> zmienne {'a', 'b', 'c', 'k', 'm', 'n', 'u', 'v', 'w', 'x', 'y', 'z'};
assert(zmienne.size());
copy(zmienne.cbegin(), prev(zmienne.cend()), ostream_iterator<char>(cout, "+"));
cout << zmienne.back() << "\n";

W C++20 z ranges będzie jakiś sensowniejszy join na range'ach, ale na razie za słabo z dokumentacją żebym na szybko szukał.
https://wandbox.org/permlink/czZ4nUmPKK8ADBCE

1

Innym pomysłem jest użycie przeładownia operatora <<, którego w funkcji main można zgrabnie połączyć z resztą wyrażenia.

#include <iostream>
#include <vector>
 
using namespace std;

ostream& operator<<( ostream& out , const vector<char>& data )
{
    for( auto iter {data.cbegin()} ; iter!=data.cend() ; ++iter ) out << *iter << ((iter==data.cend()-1)?"":"+");    
    return out;
}

int main() 
{
    vector<char> zmienne {'a', 'b', 'c', 'k', 'm', 'n', 'u', 'v', 'w', 'x', 'y', 'z'};
    
    cout << zmienne << " <= 100" << endl;
    return 0;
}

https://wandbox.org/permlink/Vkj2HXMMpcDDLcJs

0

a co powiecie na coś takiego ? Można zrobić z tego funkcję która wstawi znaki "+" lub jakiekolwiek inne

int main(void)
{
    std::vector<char> v={'a','b','c','d'};

    for(unsigned int i=0; i<v.size(); i++){

        if(i%8){
            std::cout << "+";
        }
        std::cout << v.at(i);
    }

    std::cout << std::endl;

    return 0;
}

screenshot-20200703121153.png

screenshot-20200703121231.png

2

@TomaszLiMoon: dzięki za uwagę, poniżej poprawiony kod - tym razem działa dla dowolnej ilości

int main(void)
{
    std::vector<char> v={'a','b','c','d','e','f','g','h','i','j'};

    for(unsigned int i=0; i<v.size(); i++){

        if(i%v.size()){
            std::cout << "+";
        }
        std::cout << v.at(i);
    }

    std::cout << std::endl;

    return 0;
}
0
TomaszLiMoon napisał(a):

Innym pomysłem jest użycie przeładownia operatora <<, którego w funkcji main można zgrabnie połączyć z resztą wyrażenia.

#include <iostream>
#include <vector>
 
using namespace std;

ostream& operator<<( ostream& out , const vector<char>& data )
{
    for( auto iter {data.cbegin()} ; iter!=data.cend() ; ++iter ) out << *iter << ((iter==data.cend()-1)?"":"+");    
    return out;
}

int main() 
{
    vector<char> zmienne {'a', 'b', 'c', 'k', 'm', 'n', 'u', 'v', 'w', 'x', 'y', 'z'};
    
    cout << zmienne << " <= 100" << endl;
    return 0;
}

https://wandbox.org/permlink/Vkj2HXMMpcDDLcJs

Dzięki za sugestię, ale... jakoś nie rozumiem celu takiego przeładowania?

Chodzi mi o to, że ten kod twierdzi, że każdy wektor charów ma się wypisywać w ten sposób, że stawia znaki + pomiędzy elementami; a przecież to ma sens tylko dla bardzo konkretnego podzbioru wszystkich możliwych wektorów charów.

Jeśli już miałbym przeładowywać operator << dla wektorów charów w ogólności, to chyba wybrałbym spacje jako domyślny / neutralny separator?

czy może szukam tu problemów tam, gdzie ich nie ma?

0

Dzięki za sugestię, ale... jakoś nie rozumiem celu takiego przeładowania?

Zadajesz strasznie filozoficzne pytania, sięgające do głębin konfliktu zwięzłość(a.k.a. magiczność) vs. czytelność kodu.

  • Ortodoksyjne rozwiązanie podał kq używając std::copy();.
  • Rozwiązania iteracyjne są "szkolne", do nauki algorytmów i mogą zostać potraktowane jako C++98.
  • Przeciążenie mogłoby być częścią jakiegoś większego projektu, gdzie co dziesięć linii musiałbyś logować std::vector znaków przedzielonych czymśtam.
1

Chodzi mi o to, że ten kod twierdzi, że każdy wektor charów ma się wypisywać w ten sposób, że stawia znaki + pomiędzy elementami; a przecież to ma sens tylko dla bardzo konkretnego podzbioru wszystkich możliwych wektorów charów.

Kod można bardzo łatwo uogólnić używając dodatkowego przeładowania operatora ( wymaga C++17 ):

#include <iostream>
#include <vector>
#include <string_view>
#include <functional>

using namespace std;

ostream& operator<<( ostream& out , const vector<char>& data )
{
    for( auto iter {data.cbegin()} ; iter!=data.cend() ; ++iter ) out << *iter << " ";
    return out;
}

template< typename T >
ostream& operator<<( ostream& out , const pair<T,string_view>& pair_data )
{
    const auto& [data,delimiter] = pair_data;
    for( auto iter {data.cbegin()} ; iter!=data.cend() ; ++iter ) out << *iter << ((iter==data.cend()-1)?""s:delimiter);
    return out;
}

template< typename T >
auto operator|( const T& data , string_view delimiter )
{
    return make_pair(reference_wrapper<const T>(data),delimiter);
}

int main()
{
    vector<char> zmienne {'a', 'b', 'c', 'k', 'm', 'n', 'u', 'v', 'w', 'x', 'y', 'z'};

    cout << (zmienne|"+") << endl;
    cout << (zmienne|"-") << endl;
    cout << (zmienne|"...") << endl;
    cout << zmienne << endl;
    return 0;
}

https://wandbox.org/permlink/KkyCuadtTBV93TZi

1

To cały czas jest rozwiązywanie lokalnego problemu globalnymi środkami. Jak dla mnie anti-pattern.

2

Rozwiązanie ładne - użyj boost::join.
https://stackoverflow.com/a/1833499
Do hello-worlda bym nie wstawił, ale jak masz większy projekt to pewnie boost i tak już masz.

1

mam kilka pytań w związku z tymi wszystkimi algorytmami co podajecie.

  1. Dlaczego podajecie rozwiązanie oparte o funkcje biblioteczne napisane przez "osoby/grupy/firmy zewnętrzne" ?
  2. Dlaczego nie kładziecie nacisku na napisanie własnego algorytmu wymyślonego przez samego siebie ?
  3. Czy używanie "20" różnych funkcji do jakiejś pierdoły nie sprawia, że program wykonuje się dłużej ?
  4. Ktoś wyżej napisał, że najprawdopodobniej ja napisałem swój kod w C++18 czy jakoś tak - a jak sądzicie, jakie bebechy mają użyte przez was funkcje ? Nawet jeżeli, te funkcje będą używać funkcji napisanych w standardzie C++3000 to i tak gdzieś tam głęboko będzie pierwowzór C++ bo inaczej po prostu się nie da

Gdzieś słyszałem, że obecni programiści są coraz gorsi z tego względu, że korzystają właśnie z takich gotowców ale jak przyjdzie takiemu napisanie własnego algorytmu to łamią zęby i między innymi dlatego powstało coś takiego jak andruino (chociaż się tym na obecną chwilę nie bawię) aby programiści nauczyli się pisać dobrej jakości kod

Ogólnie ideę opartą o gotowe funkcje biblioteczne uznaję ale tylko w sytuacji gdy jest to naprawdę ogarnięty programista...

2

@zkubinski - tu odpowiem, bo ciasno w komentarzach

  1. Jeśli masz na myśli STL, to baaardzo zostałeś w tyle. Jeśli pijesz do boosta, to może rzeczywiście z armaty do komara, ale pewnie warto się zapoznać
  2. Bo zajmujemy się programowaniem profesjonalnie, a nie dla przyjemności
  3. Jeśli masz na myśli użycie templatów/STL to jest to chyba najszybszy kod w świecie jaki w ogóle można wygenerować
  4. Ludzie proponujący rozwiązania iteracyjne zamiast STL nie przechodzą rekrutacji na programistów C++. Zostają im prace w C/Embedded lub C/Linux. To jest coś, z czego wątkostwórca powinie sobie zdawać sprawę.

Oczywiście własne rozwiązania iteracyjne mają dużą "ogólnoinformatyczną" wartość edukacyjną.

Gdzieś słyszałem, że obecni programiści są coraz gorsi z tego względu

A ja zauważyłem, że w dużych projektach jak ognia unika się dawnych programistów bo nie potrafią przestrzegać wypracowanych standardów. Uważają, że jak coś zrobią po swojemu, albo "tak jak zawsze robili" to jest to Ok.

0

Ludzie proponujący rozwiązania iteracyjne zamiast STL nie przechodzą rekrutacji na programistów C+

Są takie przypadki w których iteracja jest najlepszym rozwiązaniem.

   vector<int> data(10000,0);
   iota( data.begin() , data.end() , 1 );

   int step {1} , sum {0};

   for( auto index {0} ; index<data.size() ; index+=step )
   {
       if( index%2 == 0 ) ++step;
       sum += data[index];
   }

Bez użycia zewnętrznych bibliotek ( np. range_v3 ) i używając tylko komponentów STL nie widzę możliwości (*) aby jakiś algorytm ( for_each, accumulate ) mógł poruszać się pomiędzy elementami kontenera o zmienną wartość kroków.

(*) Jeżeli się mylę to proszę mnie poprawić.

2

Rozwiązania oparte o range-based for, for_each, STL czy Boost stosuje się po to właśnie, żeby nie musieć się wczytywać w ciało pętli w poszukiwaniu takich kruczków jak powyżej.

Jeśli ciało pętli jest rozpisane na poszczególne kroki to zakładam że właśnie jest tam coś z listy poniżej:

  • skoki o zmienną liczbę kroków
  • odczyt poprzedniego/następnego elementu
  • iteracja bez pierwszego lub ostatniego elementu
  • iteracja po dwóch strukturach naraz

Jeśli tego nie ma i pętla po prostu iteruje po wszystkich elementach to czytając ją straciłem czas.

Drukowanie separatorów jest na tyle częste, że powinno być w bibliotece (tej czy innej), to nie jest jakaś wydumana pętla.

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