42 CPP Module 08 ex00에서, 반복문을 단 하나도 안쓰기 (직접 만든 함수로 STL bind1st, bind2nd 사용)

You will notice that in this particular subject, a lot of the problems you are asked to solve can be solved by NOT using standard containers and NOT using standard algorithms. However, using those is precisely the goal, and if you do not make every effort to use standard containers and algorithms wherever it’s appropriate, you WILL get a very bad grade, however functional your work may be. Please don’t be so lazy.

이 주제에서는 표준 컨테이너를 사용하지 않고 표준 알고리즘을 사용하지 않음으로써 여러분이 풀어야 하는 많은 문제를 해결할 수 있다는 것을 알 수 있습니다. 하지만, 이러한 것들을 사용하는 것이 정확히 목표이며, 적절한 곳에서 표준 컨테이너와 알고리즘을 사용하는 노력을 기울이지 않으면, 아무리 제대로 작동하더라도 매우 나쁜 점수를 받게 될 것입니다. 너무 게으르게 굴지 마세요.
An easy one to start off on the right foot...
Make a template function called easyfind, templated on a type T, that takes a T and an int.
Assume the T is a container of int, and find the first occurrence of the second parameter in the first parameter.
If it can’t be found, handle the error either using an exception or using an error return value. Take ideas from how standard containers work.
Of course, you will provide a main function that tests it thoroughly.

첫 단추를 잘 끼우기 쉬운 것...
type T로 템플릿되며, easyfind라는 이름의, T 와 int 를 받는 템플릿 함수를 만드세요.
T가 int의 컨테이너라고 가정하고, 첫 번째 매개 변수에서 두 번째 매개 변수의 첫 번째 해당 사항을 찾으십시오.
찾을 수 없는 경우, 예외 또는 오류 반환 값을 사용하여 오류를 처리합니다. 표준 컨테이너의 작동 방식에서 아이디어를 얻으십시오.
물론, 당신은 그것을 철저히 테스트하는 main 함수를 제공해야 할 것입니다.

 

과제에 따르면, typename T로 템플릿 되는 easyfind 함수를 만들어야 한다.

여기서 T는 int의 컨테이너 자료형일 것으로 가정하고, 찾아낼 int를 인자로 받는다.

 

template<typename T>
typename T::iterator easyfind(T & container, int toFind)
{
	return std::find(container.begin(), container.end(), toFind);
}

 

easyfind 함수는 매우 쉽게 std::find 함수를 활용해 만들 수 있다.

 

그러나, STL의 자료형중 std::map 자료형은 구성 요소가 std::pair<typename K, typename V> 로 되어 있다.

따라서, std::find 함수를 활용해 조건을 검사할 수 없다.

 

template<typename K, typename V>
typename std::map<K, V>::iterator easyfind(std::map<K, V> &container, int toFind)
{
	typename std::map<K, V>::iterator it = container.begin();
	for (; it != container.end(); ++it)
		if (it->second == toFind)
			return (it);
	return (container.end());
}

 

위와 같이 std::find 함수를 변형한 방법으로 반복문을 구성하면 쉽게 처리할 수 있다.

 


진짜 이렇게 하는게 맞는건가?

과제의 머릿말에 있는 문구에, 적절한 곳에서 표준 컨테이너와 알고리즘을 사용하는 노력을 기울이지 않으면, 아무리 제대로 작동하더라도 매우 나쁜 점수를 받게 될 것입니다. 라고 분명히 쓰여져 있다.

 

이 부분이 매우 마음에 걸린다.

 

사실 std::find_if 함수도 있다. 그러나 찾을 값 대신, Unary함수 (인자가 하나인 함수) 포인터를 인자로 받는다.

심지어 그 하나뿐인 인자에는 이 경우에는 std::pair 요소가 들어가게 될 것이다.

 

다시 말해, 비교할 toFind 값이 끼어들 여지가 없다.

 

그래서 find_if를 사용하기 위해 발견한 방법 중 하나로 람다 함수가 있었다.

 

template<typename K, typename V>
typename std::map<K, V>::iterator easyfind(std::map<K, V> & container, int toFind)
{
	return std::find_if(container.begin(), container.end(), 
		[toFind](std::pair<K, V> & pair) { return pair.second == toFind; });
}

 

위와 같이 람다 함수를 쓰는거다.. 하지만 람다식은 C++11 부터 지원되는 기능이다. 

아니면 boost 라이브러리를 사용해도 되는 것 같긴 한데... 당연히 42에서 허락해줄리가 없다.

다시 말해, 42의 C++98에서는 택도 없다..

 


하지만 방법은 있다.

람다도 결국 문법적 설탕의 하나일 뿐, boost에서도 가능하다는건 곧, 비슷한 기능은 C++98 만으로도 구현할 수 있다는 것.

그리고 결국, <functional> 헤더에서 내가 원하는 기능을 발견할 수 있었다.

 

std::bind1st, std::bind2nd 함수였다.

이 두 함수의 역할은 다음과 같다.

인자를 두개 받는 Binary함수 들에, 매개변수 하나를 고정시켜 넣어서, Unary함수로 만들어주는 기능을 한다.

std::bind1st는 앞 인자를 고정시키며, std::bind2nd는 뒤 인자를 고정시킨다.

 

본래는 std::less<T>, std::less_equal<T> 등 기본 지원되는 템플릿 함수에 활용되는 것으로 보여진다.

이 기본 제공되는 템플릿 함수들과 비슷하게 내가 만들 수 있다면?

 

template<typename K, typename V>
class MapFinder : public std::binary_function<std::pair<K, V>, int, bool>
{
private:
	MapFinder& operator= (MapFinder const&);
public:
	MapFinder() {}
	MapFinder(MapFinder const&) {}
	virtual ~MapFinder() {}

	bool operator()(std::pair<K, V> const& pair, int const& toFind) const
	{
		return pair.second == toFind;
	}
};

 

std::less<T> 를 참고해서, 위와 같은 파인더 클래스를 정의했다.

std::binary_function 클래스를 상속받으며, 템플릿으로 <첫번째 인자타입, 두번째 인자타입, 반환타입> 을 정의한다.

 

그리고 반환타입 operator()(첫번째 인자, 두번째 인자) const 함수를 구현하면 완성.

 

template<typename K, typename V>
typename std::map<K, V>::iterator easyfind(std::map<K, V> & container, int toFind)
{
	return std::find_if(container.begin(), container.end(), std::bind2nd(MapFinder<K, V>(), toFind));
}

template<typename K, typename V>
typename std::multimap<K, V>::iterator easyfind(std::multimap<K, V> & container, int toFind)
{
	return std::find_if(container.begin(), container.end(), std::bind2nd(MapFinder<K, V>(), toFind));
}

template<typename K, typename V>
typename std::unordered_map<K, V>::iterator easyfind(std::unordered_map<K, V> & container, int toFind)
{
	return std::find_if(container.begin(), container.end(), std::bind2nd(MapFinder<K, V>(), toFind));
}

template<typename K, typename V>
typename std::unordered_multimap<K, V>::iterator easyfind(std::unordered_multimap<K, V> & container, int toFind)
{
	return std::find_if(container.begin(), container.end(), std::bind2nd(MapFinder<K, V>(), toFind));
}

 

이후 위와 같이 map 컨테이너들 각각에 모두 적용될 수 있게끔 각 iterator 타입을 모두 선언해준다.

또한 std::bind2nd를 활용해 Unary함수를 만들었으므로, std::find_if 함수를 사용하여 검색을 수행할 수 있게 되었다!

댓글

Designed by JB FACTORY