// Paweł Zubrycki, K3I3
//	Algorytm for_each

/*	Algorytm for_each umożliwia dostęp, przetworzenie i modyfikację każdego
	elementu kontenera, w tym również zwykłej tablicy. Zaliczany jest do 
	algorytmów niemodyfikujących, ale może być też zaliczany do algorytmów 
	modyfikujących.
	Algorytm for_each jest używany zamiast pętli for, aby uzwięźlić i poprawić
	czytelność kodu przy iteracji przez kontenery. Jego użycie jednoznacznie 
	określa intencje autora i ogranicza rozwlekłość kodu. 

	Nagłówek:			*/
#		include<algorithm>
/*			
	Deklaracja for_each:
		template<class InputIterator, class UnaryProc>
		UnaryProc for_each(InputIterator begin, InputIterator end, UnaryProc operation)

	Parametry:
		begin		- pierwszy element kontenera
		end		- element za ostatnim w kontenerze
		operation	- operacja wykonywana na elementach kontenera. Może być obiektem funkcyjnym
	
	Wartość zwracana:
		Kopia operation. Jeśli operation było obiektem funkcyjnym zmieniającym stan 
		podczas iteracji, for_each zwróci ten obiekt po przetworzeniu.
	
	Działanie:
		Wywołuje operation(elem) dla każdego elementu z zakresu [begin, end). operation 
		może być zwykłym wskaźnikiem na funkcję, jak i obiektem funkcyjnym. Algorytm po
		przetworzeniu zwraca operation.

	Nagłówki pomocnicze:		
*/
#include<iostream>
#include<string>
#include<map>
#include<boost/lambda/lambda.hpp>
#include<boost/lambda/bind.hpp>

using namespace std;
using namespace boost::lambda;
namespace b = boost;
		
/*/ Algorytm for_each jako algorytm niemodyfikujący: 
	
	for_each może zostać użyte do wypisania wszystkich elementów kontenera.
	
	W przykładzie posłużymy się algorytmem for_each do wyliczenia średniej ważonej. Skorzystamy z tego, 
	że for_each zwraca operację, która może być przetworzonym obiektem funkcyjnym.
*/
//	Obiekt funkcyjny obliczający średnią ważoną elementów multimapy zapisanych w formacie [waga, liczba]
class Weighted_mean{
	double value_;
	unsigned int n_;
public:
	Weighted_mean(): value_(0.0), n_(0){}
	void operator()(pair<unsigned int, double> factor){ 
		value_ += factor.first * factor.second;
	    	n_ += factor.first;
	}
	operator double(){
		if(n_ == 0) return 0.0;
		return value_/n_;
	}
};

/*/ Algorytm for_each jako algorytm modyfikujący:

	Kolejny przykład pokazuje jak można zmodyfikować zawartość kontenera, np. chcąc dodać podatek
	do produktów:
*/
//	Obiekt funkcyjny służący do wypisania produktów 
class PrintProducts{
	unsigned int count_;	
public:
	// Konstruktor przyjmuje parametr wyświetlany przed listą produktów
	explicit PrintProducts(std::string s = ""): count_(0) { if(!s.empty()) cout << s << endl; }
	void operator()(pair<string, double> a){
		cout << ++count_ << ". " << a.first << "\tcena: " << a.second << "zl" << endl; 
	}
};
	
int main(){
	locale pl("pl_PL.ISO-8859-2");
	cout.imbue(pl);
	
	cout << "PRZYKŁAD 1\n" << endl;
	// Multimapa ocen, gdzie kluczami są wagi
	typedef multimap<unsigned int, double> Oceny;
	Oceny oceny;
	oceny.insert(Oceny::value_type(4, 3.0));// pary [waga, ocena]
	oceny.insert(Oceny::value_type(3, 5.0));
	oceny.insert(Oceny::value_type(1, 5.0));
	cout << "Średnia ocen: \n";
	// Aby nie tworzyć prostych obiektów funkcyjnych jednorazowego użytku można użyć "magii" biblioteki boost:lambda 
	for_each(oceny.begin(), oceny.end(), 
		cout << constant("Waga: ") << bind(&Oceny::value_type::first, _1) << constant(", Ocena: ") 
			<< bind(&Oceny::value_type::second, _1) << constant('\n')); 

	cout << "\nwynosi: " << static_cast<double>(for_each(oceny.begin(), oceny.end(), Weighted_mean())) << endl;
	cout << "\nPRZYKŁAD 2\n" << endl;	
	
	typedef map<string, double> map_type;
	map_type koszyk;

	//Dodajemy produkty
	koszyk.insert(map_type::value_type("skarpety", 5.0));
	koszyk.insert(map_type::value_type("jeansy", 149.99));
	//Ustawiamy parametry strumienia wyjściowego
	cout.precision(2);
	cout.setf(ios::fixed);
	for_each(koszyk.begin(), koszyk.end(), PrintProducts("Ceny netto: "));
	// W tym miejscu znowu korzystamy z boost::lambda. Mnożymy każdą cenę(składowa pair<string, double>::second) przez 1.22  
	for_each(koszyk.begin(), koszyk.end(), (bind( &map_type::value_type::second, _1) *= 1.22));
	for_each(koszyk.begin(), koszyk.end(), PrintProducts("Ceny brutto: "));
	return 0;
}

