Jump to content

SUBIECTE NOI
« 1 / 5 »
RSS
Nu pot activa Memory Integrity

Supratensiuni accidentale

Cuțit/ briceag drumetie

Cum am acces la o parte dintr-un ...
 Mother's Day

Recomandare aparat de vidat alime...

Izolatie exterioara casa parter P...

Cuvinte si expresii neclare
 Mod de lucru Purmo Tempco Digital...

Samsung S90C vs LG C3

Problema sunet RCS

Amortizor sertare bucatarie
 Codrea Pallady

Blocurile goale! Orase in car...

Motorul pe benzina 1.0 SCe65

Mostenire In 1986
 

[C++] Parcurgerea elegantă a matricilor, în orice ordine

- - - - -
  • Please log in to reply
No replies to this topic

#1
dani.user

dani.user

    Guru Member

  • Grup: Senior Members
  • Posts: 30,252
  • Înscris: 24.02.2007
Probleme ce implica matrici sunt printre cele mai frecvente din liceu, si nu numai.

Sa luam un exemplu: https://forum.softpe...onacci-matrice/ . Cerinta, pe scurt: popularea unei matrici cu un sir Fibonacci, insa matricea trebuie parcursa intr-o ordine mai aparte.

Si unde-i problema?

Elevul incepe, probabil, sa scrie diverse for'uri, pentru a parcurge matricea in ordinea dorita, amesteca apoi logica cu generarea sirului lui Fibonacci rezultand:

  • Fie un amalgam care-l zapaceste de elev, acesta nereusind sa rezolve problema pana la capat (cum se intampla in topicul indicat)
  • Fie un amalgam functional, dar aproape ilizibil de oricine altcineva, astfel incat urmatorul elev ce are aceeasi problema nu intelege rezolvarea si-o ia de la capat.

De ce se intampla asta?

Fiindca elevii pun totul la gramada si nu stiu/vor/constientizeaza ca probleme diferite ar trebui rezolvate separat.

  • O matrice o parcurgi intr-un anume fel indiferent de ce citesti din/scrii in ea
  • Sirul lui Fibonacci il generezi intr-un anume fel indiferent ce faci apoi cu el

Se poate rezolva mai elegant?

Evident

Cum?

C++ abstractizeaza iterarea prin structuri de date cu ajutorul iteratorilor. Drept urmare, de ce sa nu folosim iteratori si pentru a parcurge matrici?

Ne aducem aminte ca o matrice gen int[3][3] e asezata secvential in memorie, ca un sir de 3*3 = 9 elemente. Asa cum putem folosi un pointer pentru a ne plimba prin sir, la fel putem folosi un pointer pentru a ne plimba prin matrice.

Iteratorul practic va avea un comportament asemenea unui astfel de pointer. Va retine:
  • Pointerul spre datele din matrice
  • Dimensiunile matricii
  • Pozitia curenta
  • O strategie de parcurgere a matricii

template<typename T, typename Strategy>
class MatrixIterator
{
public:
	typedef std::random_access_iterator_tag iterator_category;
	typedef T value_type;
	typedef ptrdiff_t difference_type;
	typedef T* pointer;
	typedef T& reference;
	MatrixIterator(T* matrix, size_t width, size_t height, Strategy strategy)
		: matrix_(matrix), width_(width), height_(height), current_index_(0), strategy_(strategy)
	{}
	template<size_t Width, size_t Height>
	explicit MatrixIterator(T (&matrix)[Width][Height], Strategy strategy)
		: MatrixIterator(matrix, Width, Height, strategy)
	{}
	MatrixIterator(const MatrixIterator& other) = default;
	MatrixIterator(MatrixIterator&& other) = default;
	MatrixIterator& operator=(const MatrixIterator& other) = default;
	MatrixIterator& operator=(MatrixIterator&& other) = default;
	T& operator*() const
	{
		return this->operator[](0);
	}
	T& operator[](int offset) const
	{
		return matrix_[strategy_(width_, height_, current_index_ + offset)];
	}
	MatrixIterator& operator++()
	{
		current_index_ += 1;
		return *this;
	}
	MatrixIterator operator++(int)
	{
		MatrixIterator temp = *this;
		++*this;
		return temp;
	}
	MatrixIterator& operator--()
	{
		current_index_ -= 1;		   
		return *this;
	}
	MatrixIterator operator--(int)
	{
		MatrixIterator temp = *this;
		--*this;
		return temp;
	}
	MatrixIterator& operator+=(int n)
	{
		current_index_ += n;
		return *this;
	}
	MatrixIterator& operator-=(int n)
	{
		return *this += -n;
	}
	MatrixIterator operator+(int n) const
	{
		MatrixIterator copy(*this);
		return copy += n;
	}
	MatrixIterator operator-(int n) const
	{
		MatrixIterator copy(*this);
		return copy -= n;
	}
	difference_type operator-(const MatrixIterator& other) const
	{
		return current_index_ - other.current_index_;
	}
	bool operator==(const MatrixIterator& other) const
	{
		return std::tie(width_, height_, current_index_, strategy_)
			== std::tie(other.width_, other.height_, other.current_index_, strategy_);
	}
	bool operator!=(const MatrixIterator& other) const
	{
		return !(*this == other);
	}

	bool operator<(const MatrixIterator& other) const
	{
		return std::tie(width_, height_, current_index_, strategy_)
			 < std::tie(other.width_, other.height_, other.current_index_, strategy_);
	}
	bool operator>(const MatrixIterator& other) const
	{
		return std::tie(width_, height_, current_index_, strategy_)
			 > std::tie(other.width_, other.height_, other.current_index_, strategy_);
	}
	bool operator<=(const MatrixIterator& other) const
	{
		return !(*this > other);
	}
	bool operator>=(const MatrixIterator& other) const
	{
		return !(*this < other);
	}
private:
	T* matrix_;
	size_t width_;
	size_t height_;
	size_t current_index_;
	Strategy strategy_;
};


Pare complicat la prima vedere, dar nu prea face altceva decat sa reproduca operatii primitive.
Atentie, iteratorul trebuie folosit cu grija. Nu se verifica accesul la zone de memorie nedorite daca sunt depasite limitele.

Constructorul are cam multi parametrii?

Hai sa scriem niste functii ajutatoare:

template<typename T, typename Strategy>
MatrixIterator<T, Strategy> make_matrix_iterator(T* matrix, size_t width, size_t height, Strategy strategy)
{
	return MatrixIterator<T, Strategy>(matrix, width, height, strategy);
}

template<typename T, typename Strategy, size_t Width, size_t Height>
MatrixIterator<T, Strategy> make_matrix_iterator(T(&matrix)[Width][Height], Strategy strategy)
{
	return make_matrix_iterator<T, Strategy>(matrix[0], Width, Height, strategy);
}

template<typename T, typename Strategy>
MatrixIterator<T, Strategy> make_matrix_iterator_end(T* matrix, size_t width, size_t height, Strategy strategy)
{
	auto result = make_matrix_iterator(matrix, width, height, strategy);
	result += width * height;
	return result;
}

template<typename T, typename Strategy, size_t Width, size_t Height>
MatrixIterator<T, Strategy> make_matrix_iterator_end(T(&matrix)[Width][Height], Strategy strategy)
{
	return make_matrix_iterator_end<T, Strategy>(matrix[0], Width, Height, strategy);
}


Si cum ma ajuta asta sa parcug matricea intr-o ordine diferita?

Iteratorul tine evidenta pozitiei curente intr-o matrice de dimensiuni date. Cand ii este cerut in element, el nu va folosi direct pozitia curenta ci va cere unei strategii de iterare ce pozitie sa foloseasca defapt.

Daca se doreste iterarea in ordine normala sus/jos, stanga/dreapta, strategia nu face altceva decat sa returneze pozitia curenta:

size_t normal_matrix_order(size_t width, size_t height, size_t current_index)
{
	return current_index;
}


Daca insa se vrea, de exemplu, iterarea in ordine inversa, jos/sus, dreapta/stanga, strategia calculeaza o alta pozitie:

size_t reverse_matrix_order(size_t width, size_t height, size_t current_index)
{
	return width * height - current_index - 1;
}


Cum folosesc asta?

int matrix[3][3] =
{
	{1, 2, 3},
	{4, 5, 6},
	{7, 8, 9}
};

auto start = make_matrix_iterator(matrix, &normal_matrix_order);
auto end = make_matrix_iterator_end(matrix, &normal_matrix_order);

for (auto it = start; it != end; ++it)
{
	std::cout << *it << ' ';
}


Quote

1 2 3 4 5 6 7 8 9

Ordine inversa:

int matrix[3][3] =
{
	{1, 2, 3},
	{4, 5, 6},
	{7, 8, 9}
};

auto start = make_matrix_iterator(matrix, &reverse_matrix_order);
auto end = make_matrix_iterator_end(matrix, &reverse_matrix_order);

for (auto it = start; it != end; ++it)
{
	std::cout << *it << ' ';
}


Quote

9 8 7 6 5 4 3 2 1

S-a schimbat ceva? Doar am zis ca vreau sa iterez in alta ordine, indicand strategia folosita

Pot itera si in ordinea de mai sus?

Definesc o strategie noua:

size_t alternate_matrix_order(size_t width, size_t height, size_t current_index)
{
	auto x = current_index % width;
	auto y = current_index / height;

	if (y % 2 == 0)
	{
		//normal order
		return current_index;
	}
	else
	{
		//alternate order
		auto firstInRow = y * height;
		auto offsetInRow = width - x - 1;
		return firstInRow + offsetInRow;
	}
}

int matrix[3][3] =
{
	{1, 2, 3},
	{4, 5, 6},
	{7, 8, 9}
};

auto start = make_matrix_iterator(matrix, &alternate_matrix_order);
auto end = make_matrix_iterator_end(matrix, &alternate_matrix_order);

for (auto it = start; it != end; ++it)
{
	std::cout << *it << ' ';
}


Quote

1 2 3 6 5 4 7 8 9

Am vazut cum citesc elemente din matrice, dar cum le modific?

Iteratorul permite si modificarea elementului curent.

Exemplul de mai jos populeaza, in ordine inversa, o matrice cu puteri de-a lui 2, iar apoi le afiseaza in ordinea obisnuita a matricii

int powers_of_two[3][3];
auto powers_of_two_start = make_matrix_iterator(powers_of_two, &reverse_matrix_order);
auto powers_of_two_end = make_matrix_iterator_end(powers_of_two, &reverse_matrix_order);

int current_value = 1;

for (auto it = powers_of_two_start; it != powers_of_two_end; ++it)
{
	*it = current_value;
	current_value *= 2;
}

powers_of_two_start = make_matrix_iterator(powers_of_two, &normal_matrix_order);
powers_of_two_end = make_matrix_iterator_end(powers_of_two, &normal_matrix_order);

for (auto it = powers_of_two_start; it != powers_of_two_end; ++it)
{
	std::cout << *it << ' ';
}


Quote

256 128 64 32 16 8 4 2 1

Combinatiile sunt atat de flexibile incat se pot copia elementele dintr-o matrice, parcursa intr-un anumit mod, in alta matrice, parcursa in alt mod:

int matrix[3][3] =
{
	{1, 2, 3},
	{4, 5, 6},
	{7, 8, 9}
};

auto start = make_matrix_iterator(matrix, &alternate_matrix_order);
auto end = make_matrix_iterator_end(matrix, &alternate_matrix_order);


int destination_matrix[3][3];

auto destination_start = make_matrix_iterator(destination_matrix, &reverse_matrix_order);

std::copy(start, end, destination_start);


Attached File  Untitled.png   7.23K   22 downloads

Ce fac daca am o matrice de siruri de caractere?

Acelasi lucru, codului de mai sus nu-i pasa ce stochezi in matrice.

Cool Posted Image

Anunturi

Chirurgia spinală minim invazivă Chirurgia spinală minim invazivă

Chirurgia spinală minim invazivă oferă pacienților oportunitatea unui tratament eficient, permițându-le o recuperare ultra rapidă și nu în ultimul rând minimizând leziunile induse chirurgical.

Echipa noastră utilizează un spectru larg de tehnici minim invazive, din care enumerăm câteva: endoscopia cu variantele ei (transnazală, transtoracică, transmusculară, etc), microscopul operator, abordurile trans tubulare și nu în ultimul rând infiltrațiile la toate nivelurile coloanei vertebrale.

www.neurohope.ro

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

Forumul Softpedia foloseste "cookies" pentru a imbunatati experienta utilizatorilor Accept
Pentru detalii si optiuni legate de cookies si datele personale, consultati Politica de utilizare cookies si Politica de confidentialitate