잡담소장소

[PHP] 재귀문 연습 본문

Study ;3

[PHP] 재귀문 연습

유부뽀 2020. 7. 27. 14:35

업무 중 for문이 4중첩되는 코드를 만들게 됐는데 아무리 생각해도 너무 구린 것 같아 재귀문으로 리팩토링을 시도했다

손으로 노트에 써보고 머리를 굴려봐도 제대로 만들어지지 않아서 너무 짜증나서 포기했다가 

머리속에서 뚝딱 만들어내는 천재가 아니라는 내 자신을 한탄(?)하며ㅠㅠ 단계별로 차근차근 만들어보기로 했다.

 

변수명은 다 바꿔놨는데 암튼 대충 이런 모양이었다

private function refineData($data){
	$resultData1 =array();
	$resultData2 =array();
	$totalSum = 0;
	$totalCnt = 0;
	$idx0 = 0;
	foreach( $data as $name0=>$list0 ){
		$resultData1['member'][] = array( 'id'=>$name0Mapper[$name0], 'name'=>$name0, 'resultSum'=>0, 'member'=>array());
		$resultData2['member'][] = array( 'id'=>$name0Mapper[$name0], 'name'=>$name0, 'resultSum'=>0, 'member'=>array());
		$sum0 = 0;
		$cnt0 = 0;
		$idx1 = 0;
		foreach( $list0 as $name1=>$list1) {
			$depth0Sum = &$resultData1['member'][$idx0];
			$depth0Cnt = &$resultData2['member'][$idx0];
			$mappedName1 = $this->name1Mapper[$name1]['text'];
			$mappedId1 = $this->name1Mapper[$name1]['id'];
			$depth0Sum['member'][] = array( 'id'=>$mappedId1, 'name'=>$mappedName1, 'resultSum'=>0, 'member'=>array());
			$depth0Cnt['member'][] = array( 'id'=>$mappedId1, 'name'=>$mappedName1, 'resultSum'=>0, 'member'=>array());
			$sum1 = 0;
			$cnt1 = 0;
			$idx2= 0;
			foreach( $list1 as $name2=>$list2){
				$depth1Sum = &$depth0Sum['member'][$idx1];
				$depth1Cnt = &$depth0Cnt['member'][$idx1];
				$depth1Sum['member'][] = array( 'name'=>$name2, 'resultSum'=>0, 'member'=>array());
				$depth1Cnt['member'][] = array( 'name'=>$name2, 'resultSum'=>0, 'member'=>array());
				$sum2 = 0;
				$cnt2 = 0;
				$nameMappedList = $allNameList[$name2];
				foreach( $list2 as $obj ){
					$depth2Sum = &$depth1Sum['member'][$idx2];
					$depth2Cnt = &$depth1Cnt['member'][$idx2];
					foreach($nameMappedList as $name=>$id){
						if( $obj['id'] == $id ){
							$obj['name'] = $name;
							break;
						}
					}
					$obj['name'] = $obj['name'] !== '^' ? $obj['name'] : $obj['id'];
					$depth2Sum['member'][] = array("name"=>$obj['name'], "resultSum"=>$obj['value1']);
					$depth2Cnt['member'][] = array("name"=>$obj['name'], "resultSum"=>$obj['value2']);
					$sum2 += $obj['value1'];
					$cnt2 += $obj['value2'];
				}
				usort( $depth1Sum['member'][$idx2]['member'], array($this,'sortBySumDesc') );
				usort( $depth1Cnt['member'][$idx2]['member'], array($this,'sortBySumDesc') );
				$depth1Sum['member'][$idx2]['resultSum'] = $sum2;
				$depth1Cnt['member'][$idx2]['resultSum'] = $cnt2;
				$sum1 += $sum2;
				$cnt1 += $cnt2;
				$idx2++;
			}
			usort( $depth0Sum['member'][$idx1]['member'], array($this,'sortBySumDesc') );
			usort( $depth0Cnt['member'][$idx1]['member'], array($this,'sortBySumDesc') );
			$depth0Sum['member'][$idx1]['resultSum'] = $sum1;
			$depth0Cnt['member'][$idx1]['resultSum'] = $cnt1;
			$sum0 += $sum1;
			$cnt0 += $cnt1;
			$idx1++;
		}
		usort( $resultData1['member'][$idx0]['member'], array($this,'sortBySumDesc') );
		usort( $resultData2['member'][$idx0]['member'], array($this,'sortBySumDesc') );
		$resultData1['member'][$idx0]['resultSum'] = $sum0;
		$resultData2['member'][$idx0]['resultSum'] = $cnt0;
		$totalSum += $sum0; 
		$totalCnt += $cnt0; 
		$idx0++;
	}
	usort( $resultData1['member'], array($this,'sortBySumDesc') );
	usort( $resultData2['member'], array($this,'sortBySumDesc') );
	$resultData1['resultSum'] = $totalSum; 
	$resultData2['resultSum'] = $totalCnt; 
	return array("resultSum"=>$resultData1, "resultCnt"=>$resultData2);
}

 

 

재귀문을 만들기 위해서 아래와 같이 작업하였다.

  1. for문을 4개의 function으로 분리
  2. 공통인 부분과 다른 부분을 확인하여 재귀문 내에서 분리할 부분과 합칠 부분 확인
    1. member array 생성
    2. sum, cnt 생성
    3. member array 정렬
    4. sum, cnt를 상위 totalSum, totalCnt에 합산
    5. member array 생성시의 id, name 지정 방식

정리의 정리끝에 완성된 코드

private function refineData($data){
	$arrSum =array(); 
	$arrCnt =array();
    
	$depth = 0;
	$ret = $this->recursiveData($depth, $arrSum, $arrCnt, 0, 0, $data);
	$this->mappingAndSortingData($ret, $arrSum, $arrCnt);
	return array("sum"=>$arrSum, "cnt"=>$arrCnt);
}

private function recursiveData($depth, $arrSum, $arrCnt, $totalSum, $totalCnt, $data, $nameMappedList){
	$idx = 0;
	foreach( $data as $subName=>$subList ){
		$nameMappedList = $depth == 2 ? $this->allNameList[$subName] : null;
		$currentName = $depth !== 3 ? $subName : $this->getLeafSubName($subList, $nameMappedList);
		$currentList = $depth == 3 ? $subList : null;
		$this->setNameOfList($depth, $currentName, $arrSum, $arrCnt, $currentList);

		$curSum = &$arrSum['member'][$idx];
		$curCnt = &$salesCnt['member'][$idx];

		$ret = $this->recursiveData($depth+1, $curSum, $curCnt, 0, 0, $subList, $nameMappedList);
		if( $depth !== 3 ){
			$this->mappingAndSortingData($ret, $curSum, $curCnt);
			$totalSum += $arrSum['member'][$idx]['sum']; 
			$totalCnt += $arrCnt['member'][$idx]['sum']; 
			$idx++;
		} else {
			$totalSum += $subList['value1'];
			$totalCnt += $subList['value2'];
		}
	}
	return array($arrSum, $arrCnt, $totalSum, $totalCnt);
}
private function setNameOfList($depth, $subName, &$arrSum, &$arrCnt, $subList){
	switch($depth){
		case 0:
			$arrSum['member'][] = array( 'id'=>$this->name0Mapper[$subName], 'name'=>$subName, 'sum'=>0, 'member'=>array());
			$arrCnt['member'][] = array( 'id'=>$this->name0Mapper[$subName], 'name'=>$subName, 'sum'=>0, 'member'=>array());
			break;
		case 1:
			$arrSum['member'][] = array( 'id'=>$this->name1Mapper[$subName]['id'], 'name'=>$this->name1Mapper[$subName]['text'], 'sum'=>0, 'member'=>array());
			$arrCnt['member'][] = array( 'id'=>$this->name1Mapper[$subName]['id'], 'name'=>$this->name1Mapper[$subName]['text'], 'sum'=>0, 'member'=>array());
			break;
		case 2:
			$arrSum['member'][] = array( 'name'=>$subName, 'sum'=>0, 'member'=>array());
			$arrCnt['member'][] = array( 'name'=>$subName, 'sum'=>0, 'member'=>array());
			break;
		case 3:
			$arrSum['member'][] = array( "name"=>$subName, "sum"=>$subList['value1']);
			$arrCnt['member'][] = array( "name"=>$subName, "sum"=>$subList['value2']);
			break;
	} 
}
private function mappingAndSortingData($ret, &$arrSum, &$arrCnt){
	usort( $ret[0]['member'], array($this,'sortBySumDesc') );
	usort( $ret[1]['member'], array($this,'sortBySumDesc') );
	$arrSum = $ret[0];
	$arrCnt = $ret[1];
	$arrSum['sum'] = $ret[2];
	$arrCnt['sum'] = $ret[3];
}
private function getLeafSubName($mdInfo, $nameMappedList){
	$subName = $info['name'];
	if( $info['name'] == '^' ){
		$subName = $info['id'];
	} else {
		foreach($nameMappedList as $name=>$id) {
			if( $info['id'] == $id ) return $name;
		}
	}
	return $subName;
} 

 

처음엔 pass by reference(&%value형식)로 재귀함수에 돌리려 했는데 제대로 안되는데다가 return 방식이 좀더 명확하여 필요한 값들을 반환하도록 하였다.

대신 재귀 내에서 호출하는 함수들은 pass by reference로 했는데 

나중에 찾아보니 PHP에서 reference를 받아서 처리하는걸 지양하는 글이 좀 있더라..

아무래도 return 없이 값이 변경된다는게 이해가 안되서 그런듯 하다..

(팀 내 자바개발자들이 많다보니 더더욱 문제가 있을것 같네;;)

 

 

역시 재귀문은 정리해놓고 나면 원래의 모양이 상상이 안간다-_-;

코드가 깔끔해진건 만족스러운데, 내가 아닌 다른 사람이 볼때는 이해가 안간다는게 제일 큰 문제..ㅠㅠ...

내 욕심을 채우면 안되는건 아는데.. 욕심나는걸 오또케..ㅠㅠ

4중첩for문은 도저히 못봐주겠는걸...

반응형
Comments