Zadanie funkcją szablonową i kontenerem

0

Witam.

Mam takie zadanie z motywem definiowania funkcji szablonowej oraz kontenerem "list".
Zamieszczam treść.


Napisz szablon funkcji, która przyjmuje w argumencie stałą referencję na obiekt kontenera listy z biblioteki standardowej C++. Zakładamy, że lista zawiera wartości liczbowe. Funkcja powinna zwrócić (w postaci liczby naturalnej) pozycję (indeksując od 0) elementu o wartości największej (przy pustej liście - wartość -1).


Próbowałem robić zadanie - na różne sposoby, i nijak nie wychodzi! Ciągle gdzieś coś się nie chce kompilować - choć logicznie wydaje się, że powinno być dobrze. Używam Dev-C++, ale to chyba nie ma znaczenia. Nie jestem jakiś zielony z programowania, również obiektowego, tylko nie jestem oblatany w szablonach.

Jeśli ktoś jest w tym obeznany i może pomóc, prosiłbym o wskazanie/poprawienie błędów - tak, by kompilowała się funkcja, wraz z całym programem ją testującym (czasem funkcja potrafi się skompilować, a błąd wyjść dopiero przy jej wywołaniu).

Zamieszczam kod napisanego przeze mnie programu (w pierwotnej wersji). Tutaj (kompletnie nie rozumiem dlaczego) błąd kompilacji powodują deklaracje iteratorów - na początku funkcji.


#include <iostream>
#include <cstdlib>
#include <string>
#include <cmath>
#include <list>

using namespace std;



template <typename T>
int MaxWLiscie(const list<T> &lista)
{
  list<T>::iterator itMax;   //iterator na element największy
  list<T>::iterator it;

  if (lista.empty())
    return -1;

  itMax = lista.begin();
  it = lista.begin()+1;
  while (it != lista.end())
  {
    if (*it > *itMax)
      itMax = it;
    it++;
  }

  return *itMax;
}



int main()
{
  list<double> Lista;

  Lista.push_back(10);
  Lista.push_back(5);
  Lista.push_back(14);
  Lista.push_back(2);

  MaxWLiscie<double>(Lista);


  cout << endl;
  system("PAUSE >NUL");
  return 0;
}

Pozdrawiam i liczę na pomoc. :)

0

Podajesz do szablonu typ double, a powinieneś list<double>.

1

^ Niby dlaczego?

  1. Prosta zasada - jak masz const listę to używasz const_iterator.
  2. Iteratory nie muszą mieć operatora dodawania. Użyj tam zwykłego it++. Jeżeli kiedyś będziesz potrzebował przeskoczyć o więcej pozycji to użyj std::advance (które to wywołuje it++ w pętli ;)).
  3. Twój program ma zwrócić nie największą wartość, a jej pozycję.
0

Od siebie tylko dodam że tą funkcje można napisać zdecydowanie krócej. Wyszukiwanie największej wartości jest czymś dość powszechnym i w C++ są do tego dedykowane algorytmy.

Do przejrzenia :
C++ algorytmy -> http://www.cplusplus.com/reference/algorithm/

0
Tattva napisał(a):

Od siebie tylko dodam że tą funkcje można napisać zdecydowanie krócej. Wyszukiwanie największej wartości jest czymś dość powszechnym i w C++ są do tego dedykowane algorytmy.

Do przejrzenia :
C++ algorytmy -> http://www.cplusplus.com/reference/algorithm/

Zazwyczaj w tego typu zadaniach chodzi o to, żeby napisać coś co już jest.

1

Niby tak ale w zadaniu nie ma żadnych ograniczeń pod tym kątem. A że podstawą dobrego programowania jest lenistwo więc nie widzę sensu w pisaniu czegoś co już zostało napisane i przetestowane. Innymi słowy po co sobie utrudniać skoro można ułatwić, biblioteka standardowa po to jest żeby z niej korzystać.

0

jezeli nie jest sprecyzowane zeby napisac od poczatku to jest BARDZO nie rozsadne pisac to od poczatku. Dostajesz narzedzia powinienes wiedziec jak je uzyc.

Jezeli bedziesz potrzebowal wbic pare gwozdzi to najpierw bedziesz tworzyc swoj wlasny mlotek?
Tak fajnie wiedziec jak zrobic wlasny mlotek, ale prawie wszedzie te mlotki sa dostepne (szukanie maxa, w kazdym jezyku [oprocz lua] jest dostepne)

0

Aux pisze:

Podajesz do szablonu typ double, a powinieneś list<double>

Tak, moje przeoczenie, rzeczywiście logicznie wydaje się podawać list<double> , ale tak też próbowałem (z odstępem między znaczkami "<" koło siebie) i nie idzie (wywołanie powoduje błąd - jeśli funkcję się przerobi, żeby się kompilowała).

Tattva pisze:

Najlepiej wcale nie podawać typu przy wywołaniu. (...)

I docelowo tak bym nawet zrobił. Ale chcę sprawdzić że pójdzie z podaniem typu (bo chyba powinno).

Rev pisze:

  1. Prosta zasada - jak masz const listę to używasz const_iterator.

Próbowałem z const_iterator - nic nie pomogło. Próbowałem też usuwać "const" w nagłówku funkcji, a także usuwać stamtąd "&". I mimo to nie chce przejść deklaracja iteratora - chyba że zamiast list<T>::iterator napiszę np.: list<int>::iterator .

Rev pisze:

  1. Iteratory nie muszą mieć operatora dodawania. (...)

Ale operator listy akurat ma.

Rev pisze:

  1. Twój program ma zwrócić nie największą wartość, a jej pozycję.

Tak tak, moje (drobne) przeoczenie.

W odpowiedzi na liczne (jałowe dla mnie) uwagi o policzeniu maksimum gotową funkcją...
No dobra, przyznam się, że w zadaniu trzeba policzyć coś innego, a nie wartość największą, i tego się raczej nie da wyliczyć gotowymi funkcjami. Uprościłem treść zadania - tzn. kwestię tego CO trzeba obliczyć, żeby nie zamulać problemu drugorzędnymi kwestiami obliczeniowymi, tylko skupić się na istocie, tj. jak to zdefiniować, zadeklarować itp. A tu niechybnie wygenerowałem nowy problem... :)
Proszę więc przyjąć, że nie ma funkcji max_element czy innej.

1
#include <list>
#include <iostream>
using namespace std;

template<typename T>
T Max(const list<T>& source, const T& ifEmpty) {
  typename list<T>::const_iterator
    first = source.begin(),
    last  = source.end(),
    max   = source.begin();

  if(first == last) return ifEmpty;
  while(++first != last)
    if(*max < *first)
      max = first;

  return *max;
}

int main() {
  list<int> mylist { 1,3,4,1,2,7,5 };
  cout << Max(mylist, -1);
}
0

Ja sam napisałem:

Ale operator listy akurat ma [operatora dodawania - przyp. red.].

Ups, jednak nie ma.

Pingwinku, w Twoim programie mam gdzieniegdzie wątpliwości co do poprawności (to zwracanie *max przy pustej liście). Ponadto wywala błąd kompilacji w miejscu deklaracji listy w programie głównym.
Ale wreszcie mi chyba pomogłeś...
Przy okazji zauważyłem moje kolejne przeoczenia - typ wyniku funkcji (powinien być szablonowy) i brak wypisywania wyniku w programie głównym (ale to ostatnie było tak tymczasowo).
Ale przede wszystkim, z tym "typename" przed deklaracją iteratora - nie wiedziałem że coś takiego jest (coś takiego trzeba pisać).

Teraz już prawie wszystko działa. Zamieszczam kod poprawionego programu.

#include <iostream>
#include <cstdlib>
#include <list>

using namespace std;


template <typename T>
T MaxWLiscie(const list<T> &lista)
{
  typename list<T>::const_iterator itMax;   //iterator na element największy
  typename list<T>::const_iterator it;

  if (lista.empty())
    return -1;

  itMax = lista.begin();
  it = lista.begin();
  it++;
  while (it != lista.end())
  {
    if (*it > *itMax)
      itMax = it;
    it++;
  }

  return itMax-lista.begin();   // <- TO NIE PRZECHODZI
}


int main()
{
  list<double> Lista;

  Lista.push_back(11.2);
  Lista.push_back(5.3);
  Lista.push_back(14.7);
  Lista.push_back(2.5);

  cout << MaxWLiscie<double>(Lista) << endl;


  cout << endl;
  system("PAUSE >NUL");
  return 0;
}

Tylko jeszcze jedna rzecz - zwracanie wyniku funkcji (zaznaczyłem w kodzie). Czy nie ma jakiego sposobu (tzn. gotowej funkcji), żeby dostać indeks elementu listy, na który wskazuje dany iterator? Czy pozostaje zrobić to inaczej - poprzez wzięcie jakiejś liczby oznaczającej indeks i inkrementowanie jej...?

Dzięki wszystkim za dotychczasową pomoc.

0

Ponadto wywala błąd kompilacji w miejscu deklaracji listy w programie głównym.

Bo masz sredniowieczny kompilator.

if (lista.empty())
    return -1;

To nie powinno wystapic. Patrz na poprawke.

Czy nie ma jakiego sposobu (tzn. gotowej funkcji), żeby dostać indeks elementu listy

Po co chcialbys to robic? Jest niby cos takiego http://www.cplusplus.com/reference/iterator/distance/ ale wydaje sie niezbyt potrzebne.

0

No i zapomniałem jeszcze o poprawieniu parametru szablonu, przy wywołaniu.
Ale (zakładając, że zakomentuję zwracanie wyniku w funkcji - które powoduje błąd)...
Kompiluje się poprawnie z:

  cout << MaxWLiscie<double>(Lista) << endl;

(a także bez podania parametru szablonu), natomiast NIE kompiluje się z:

  cout << MaxWLiscie< list<double> >(Lista) << endl;

:(
Próbowałem jeszcze taki numer:

  cout << MaxWLiscie< const list<double> >(Lista) << endl;

ale to już całkiem źle.

0

Jeśli nie używamy algorytmów to byłoby moim zdaniem tak :

 
template<class type>
int getMaxIdx(const list<type>& range) {
    if (!range.empty()) {
        auto itMax = range.cbegin();

        for (auto it = range.begin(); it != range.end(); ++it) {
            if (*it > *itMax) {
                itMax = it;
            }
        }
        return distance (range.begin(), itMax);
    } else {
        return -1;
    }
} 

Choć i tak bym chyba wolał rozwiązanie z find_if i lambdą no ale cóż.

0

"Pingwinek" pisze:
<qoute>
<qoute>
Ponadto wywala błąd kompilacji w miejscu deklaracji listy w programie głównym.
</qoute>
Bo masz sredniowieczny kompilator.
</qoute>
Jeśli nawet to o to chodzi, to chciałym aby program przeze mnie pisany działał nie tylko na wydanym wczoraj kompilatorze.
Zresztą ten problem nie ma znaczenia, i tak w programie robię to inaczej.

"Pingwinek" pisze też:
<qoute>
<qoute>

if (lista.empty())
    return -1;
</qoute> To nie powinno wystapic. Patrz na poprawke. </qoute> To właśnie że powinno wystąpić. Patrz na treść zadania (w pierwszym poście). Inna sprawa, że gdzieśtam się pomyliłem i napisałem zwracanie wartości zamiast indeksu. Ale funkcja ma zwracać INDEKS (numer, pozycję) elementu.
2

To wiele wyjasnia...

#include <iostream>
#include <algorithm>
#include <list>
using namespace std;

template<typename T>
int Max(const list<T>& src) {
	typename list<T>::const_iterator
		first = src.begin(),
		last  = src.end(),
		max   = src.begin();
	
	if(first == last) return -1;
	while(++first != last)
		if(*max < *first)
			max = first;
			
	return distance(src.begin(), max);
}

int main() {
	list<int> mylist {12,31,2,1};
	cout << Max(mylist);
	return 0;
}
1
#include <iostream>
#include <list>

template<typename T>
int f(std::list<T> const& l) {
	if(!l.size())
		return -1;

	int pos = 0;
	std::pair<T, int> temp { *std::begin(l), pos };
	for(auto const& e : l) {
		if(e > temp.first)
			temp = { e, pos };

		++pos;
	}

	return temp.second;
}

int main()
{
	std::list<int> l { 1, 12, 3 , 4 };
	std::cout << f(l);

	return 0;
}
0

Tattva pisał:

I dobrze że MaxWLiscie< list<double> >(Lista) ci się nie kompiluje. Zwróć uwagę na to co jest typem szablonowym. W drugim przypadku próbujesz wygenerować funkcje o takim argumencie const list<list<double>>& .

OK, rzeczywiście, z tym ewentualnym podawaniem typu szablonowego przy wywołaniu, rzeczywiście powinno być <double> a nie <list<double> > . To wydaje się logiczne. Wynikło tu pewne zamieszanie (ktoś tak napisał i się zasugerowałem).

Tak w ogóle, to temat - przynajmniej z mojej strony - można uznać za zamknięty.
Dzięki wszystkim za pomoc.

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