'2009/02'에 해당되는 글 1건

  1. 2009.02.01 짧고 습관적인 코드로 패턴 만들기 (3)

짧고 습관적인 코드로 패턴 만들기

두 요소를 비교하거나 컨테이너에 들어있는 요소의 정렬할 때 키가 둘 이상인 경우가 종종 있지요. 먼저 하나를 비교하고 같을 때 두 번째 값을 비교하는 형태로 구현합니다. 그러면 아래처럼 생긴 코드가 되지요.

  1. bool greater(const Element& source, const Element& target)
  2. {
  3. if (source.first < target.first) {

  4. return true;

  5. } else if (source.first == target.first) {

  6. return source.second < target.second;

  7. }

  8. return false;

  9. }

이런 함수를 클래스 연산자로 재정의하기도 하고 유틸리티 함수로 만들기도 하는데요 오늘은 범용으로 사용할 수 있는 함수를 하나 만들어 볼까 합니다. 오늘 하고 썼다고 해서 내일 뭔가 만들지는 않습니다만.

논리표현식 적용하기

제가 복잡한 조건문을 읽기 편하게 만들기 위해 사용하는 클래스가 있습니다. 이름은 아무래도 상관없지만 여기서는 이걸 LogicalExpression 이라 부르겠습니다. And(&&)와 Or(||)가 복잡하게 섞인 조건문은 아무래도 읽기 어렵기 때문에 괄호로 싸인 각각의 묶음을 역할에 따라 이름을 붙여주려 할 때 사용합니다. 예를 들어 다음과 같은 코드가...

  1. // 버프가 취소됐거나 지속 시간이 지났으면 제거합니다
  2. if (
  3. (buff.live == false) ||
  4. (buff.live && (buff.tickInitialized + buff.tickDuration <= Time::tickNow)) {
  5. remove(buff);

  6. }

아래처럼 바뀔 수 있습니다. 

  1. LogicalExpression isDead(buff.live == false);
  2. LogicalExpression isExpired(buff.live);
  3. isExpired.and(buff.tickInitialized + buff.tickDuration <= Time::tickNow);
  4. if (isDead.or(isExpired)) {
  5. remove(buff);

  6. }

의미가 더 명확해졌죠. 이제 주석이 필요없는, 의미를 말하는 코드가 되었습니다. 비록 예를 위한 예이긴 해도 어떤 용도로 쓰는지는 아실 거에요. 그럼 우리가 처음에 만든 비교문을 LogicalExpression을 써서 다시 만들되 범용성을 갖도록 해봅시다.

  1. template< typename _Ty1, typename _Ty2 >
  2. bool greater(_Ty1 s1, _Ty1 t1, _Ty2 s2, _Ty2 t2)
  3. {
  4. LogicalExpression greaterFirst(s1 < t1);
  5. LogicalExpression greaterSecond(s1 == t1);
  6. greaterSecond.and(s2 < t2);
  7. return greaterFirst.or(greaterSecond);
  8. }

코드는 더 짧고 명확해졌고, 의도한 건 아니지만 복잡성이 줄어드는 부작용도 일어났습니다. 에전 함수는 Element에 대해서만 사용할 수 있었지만 새로운 함수는 그렇지 않고요. 대신 예전보다 부르기가 번잡스러우니 일을 줄여주는 매크로를 만들 수도 있겠지요.

  1. #define GREATER_2(s,t,a,b) greater(s.a, t.a, s.b, t.b)
  2. {
  3. if (GREATER_2(source, target, first, second)) {

  4. // target이 더 크다!

  5. }

  6. }

뭔가 신경쓰인다

우리가 만든 새로운 greater 함수에서 첫 번째 요소가 두 번 나오잖아요? 큰지 한 번 보고 같은지 한 번 보니까요. 신경쓰이네요. 비슷한 구문이 반복되면 실수할 여지도 생기고, 이유야 어쨌든 왠지 기분 나쁩니다. 비교하는 값은 동일하고 부등호만 바꿔볼 수 있는 객체가 있으면 좋겠군요. 이걸 PairEvaluate 라고 합시다. 이 객체에 값을 한 번만 넣고 greater()나 equal() 메소들를 부르면 될 것 같네요.

 

  1. #define GREATER_2(s,t,a,b) greater(PairEvaluate(s.a, t.a), PairEvaluate(s.b, t.b))
  2. {
  3. if (GREATER_2(source, target, first, second)) {

  4. // target이 더 크다!

  5. }

  6. }

    template< typename _Ty1, typename _Ty2 >
  7. bool greater(const PairEvaluate< _Ty1 >& first, const PairEvaluate< _Ty2 >& second)
  8. {
  9. LogicalExpression greaterFirst(first.greater());
  10. LogicalExpression greaterSecond(first.equal());
  11. greaterSecond.and(second.greater());
  12. return greaterFirst.or(greaterSecond);
  13. //return first.greater() || (first.equal() && second.greater());
  14. }

마지막 줄에는 LogicalExpression 없이 동일한 의미를 하는 코드를 적어봤는데요, 오히려 이편이 읽기 좋군요.

이 글은 스프링노트에서 작성되었습니다.

신고
Trackback 1 Comment 3

prev 1 next


티스토리 툴바