문제

지표 번호성격 유형

1번 지표 라이언형(R), 튜브형(T)
2번 지표 콘형(C), 프로도형(F)
3번 지표 제이지형(J), 무지형(M)
4번 지표 어피치형(A), 네오형(N)

 

choices뜻

1 매우 비동의
2 비동의
3 약간 비동의
4 모르겠음
5 약간 동의
6 동의
7 매우 동의

입출력 예

survey  choices  result
["AN", "CF", "MJ", "RT", "NA"] [5, 3, 2, 7, 5] "TCMA"
["TR", "RT", "TR"] [7, 1, 3] "RCJA"

 

 


코드

package solution;
import java.util.HashMap;
import java.util.Map;

class Solution {
	//점수 저장 맵
	Map<Character, Integer> surveyMap = new HashMap<>();
	// 설문지에 맞는 점수배열, 앞에 아무숫자를 넣어 배열보정을 하면 나중에 -1 을 안해도된다.
	int[] scoreArr = {0,3,2,1,0,1,2,3}; 
	String[] survey;
	int[] choices;
	
	public static void main(String[] args) {
		Solution solution = new Solution();
		String[] survey = {"AN", "CF", "MJ", "RT", "NA"};
		int[] choices = {5, 3, 2, 7, 5};
		System.out.println(solution.solution(survey, choices));
	}
	
	public String solution(String[] survey, int[] choices) {
    	this.survey = survey;
    	this.choices = choices;
    	
    	//맵 초기화
    	for(char key : "RTCFJMAN".toCharArray()) {
    		surveyMap.put(key, 0);
    	}
    	
    	//검사지에 따른 점수 계산
    	for(int i=0; i<survey.length;i++) {
    		String tmp = survey[i];
    		char l = tmp.charAt(0);
    		char r = tmp.charAt(1);
			calculate(l,r,i);
    	}
    	
    	return result();
    }
	
	private String result() {
		StringBuilder sb =new StringBuilder();
		// 조건식 수정을 하면 동일할 경우 무엇을 리턴할지를 결정할 수 있다.
    	sb.append( sum('R','T')>=0?"R" : "T");
    	sb.append( sum('C','F')>=0?"C" : "F");
    	sb.append( sum('J','M')>=0?"J" : "M");
    	sb.append( sum('A','N')>=0?"A" : "N");
    	System.out.println(surveyMap);
        return sb.toString();
	}
	
	//점수를 계산한다.
	private void calculate(char l,char r, int i) {
		if(choices[i]<4) {
			accumulateValue(l, scoreArr[choices[i]]);
		}else {
			accumulateValue(r, scoreArr[choices[i]]);
		}
	}
	//계산된 결과에 따라 점수를 누산한다.
	private void accumulateValue(char key, int value) {
		surveyMap.put(key, surveyMap.get(key)+value);
	}
	
	private int sum(char l,char r) {
		return surveyMap.get(l) - surveyMap.get(r);
	}
	
}

결과

{A=1, R=0, C=1, T=3, F=0, J=0, M=2, N=1}
TCMA

choices 인자가 1~7 가 오는데, 그에 따른 점수는 3, 2,1, 0, 1, 2, 3 이다.

이를 보정하기 위해 choices 배열 크기에 맞는 점수 배열을 별도로 만들면, 좋다.

'자료구조&알고리즘 > Level1' 카테고리의 다른 글

완주하지 못한 선수  (1) 2022.10.12
숫자 문자열과 영단어  (0) 2022.10.01
신고 결과 받기  (0) 2022.09.24
올바른 괄호  (1) 2022.09.23
크레인 인형뽑기 게임  (1) 2022.09.21

서술적인 이름을 사용하라

함수가 하는 일을 좀 더 잘 표현하기

코드를 읽으면 짐작했던 기능을 수행한다면 좋은 이름이다.

이름이 길어도 두려워하지 말라, 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.

특히, 길고 서술적인 이름이 길고 서술적인 "주석"보다 좋다.

요즘은 IDE 기능을 이용하면 이름 변경이 쉬우므로 자주 변경하는 것에 두려워할 필요 없다.

 

 

 

함수 인수

함수에서 가장 이상적인 인수 개수는 0 개다.

다음은 단항, 다음은 이항이다.

 

인수는 함수 개념을 이해하기 어렵게 한다.

인수가 있다면 내부구조를 봐서 무슨 일을 하는지 세부사항을 확인해야 한다.

 

또한 테스트 관점에서 함수 인수는 테스트를 더욱 어렵게 한다.

인수가 3개 이상이라면 모든 경우의 수를 테스트하기 힘들어진다.

인수가 없다면 쉬울 것이다.

 

인수가 두 개 이상부턴 인수의 순서도 생각해야 한다.

 

결과적으로 최선은 인수가 없는 것이고, 차선은 인수가 1개인 것이다.

 

 

많이 쓰는 단항 형식

  • 인수에 질문을 던지는 경우
    boolean fileExists("file")
  • 인수를 뭔가로 변환해 결과를 반환하는 경우
    InputStream fileOpen("file")

위와 같은 경우가 아니라면 단항 함수도 가급적 피하는 것이 좋다

인수를 무언가로 변환하는 함수의 경우 이름을 명확히 해야 한다.

좋은 예시

Integer parseInt()  

딱 봐도 반환 결과가 Integer 형일 것을 알 수가 있다.

 

 

 

플래그 인수

플래그 인수는 추하다. 함수로 boolean 값을 넘기는 것은 끔찍하다.

함수는 한 가지만 잘해야 하는데, 플래그 인수가 true/false에 따라 동작을 달리한다는 것은 SRP를 위배한다. 

또한, 외부에서 값을 받아 객체가 조작된다는 것은 의존성이 높다는 것과 같다.

차라리 함수를 두 개로 쪼개는 것이 올바르다.

 

 

이항 함수

이항 함수부터는 인수 순서에도 주의가 필요하다.

가급적 안 쓰는 것이 좋지만, 문맥상 자연스러운 이항 함수도 분명 존재한다.

compare(a, b) ,  Objects.equeals(a, b)...

 

 

삼항 함수

당연히 이항 함수보다 훨씬 이해하기 어렵다.

순서, 주춤, 무시로 야기되는 문제가 이상 늘어난다.

 

인수 객체

인수가 많아 일부를 독자적인 인수 객체로 선언한 경우

인수 객체가 단순히 인수 개수를 줄이겠다는 눈속임으로 생각할 수 있다.

꼭 그렇지만은 않다. 인수 사용 시 객체의 이름을 꼭 붙여야 하므로 결국 개념을 표현하게 된다.

 

 

동사와 키워드

함수 인자 이름과 함수 이름은 명사/동사 쌍을 이루면 좋다.

find(item)  누가 봐도 아이템을 찾는 것이라는 것을 알 수 있다.

더 좋은 방향은 인수 순서까지 표시하는 것이다.

inputPasswordEqualsDbPassword(intputPassword, DbPassword)

 

 

부수 효과를 일으키지 마라

부수효과는 거짓말이다. 함수는 한 가지만 하겠다고 약속하고선 남몰래 다른 짓도 하니까.

클래스 변수나, 참조 변수를 수정하면, 많은 경우 시간적인 결합이나 순서 종속성을 초래한다.

특히, 부수효과를 파악하려면 함수 내부를 들여봐야지만 알 수가 있다.

따라서 어쩔 수 없는 경우는 함수 이름으로 부수효과를 알 수 있게 해야 한다.

 

 

출력 인수

appendFooter(String str)

이 함수를 호출하는 쪽 관점에서 보자.

이 함수를 호출하면 str을 바닥글로 첨부하는 것일까? 아니면, str에 바닥글을 첨부할까?

알 수가 없어 이 함수를 호출한 선언부를 찾아본다.

이는 코드를 보다가 주춤하는 행위와 동급이다. 인지적으로 거슬리므로 피해야 한다.

 

객체지향 언어에서는 출력 인수를 사용할 필요가 거의 없다. 출력 인수를 사용하라고 설계한 변수가 this이기 때문이다.

 

 

명령과 조회를 분리하라

함수는 무언가를 수행하거나 무언가에 답하거나 둘 중 하나만 해야 한다.

객체 상태를 변경하거나, 객체 정보를 반환하거나   

위 두 행위를 하나의 함수로 하는 경우를 보자

public boolean set(String attribute, String value)

이 함수는 이름이 attribute인 속성을 찾아 값을 value로 설정하고 결과를 boolean으로 리턴한다.

set("username", "kks") 

이 경우 username 속성을 찾아 kks로 설정에 성공한 결과를 반환하는 것인지

username 속성에 kks 값이 존재할 때 결과를 반환하는 것인지 모호하다.

 

함수 이름을 명확히 해 분별이 가능하게 할 수도 있지만, 애초에 명령과 조회를 분리해 혼란을 뿌리 뽑는 것이 좋다.

attributeExists("username")  조회

setAttribute("username", "kks")  명령

 

 

 

오류 코드보다 예외를 사용하라

명령 함수에서 오류 코드를 반환하는 방식은 명령/조회 분리 규칙을 미묘하게 위반한다.

오류 코드 대신 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리되므로 코드가 깔끔해진다.

 

 

 

Try/Catch 블록 뽑아내기

원래 try/catch 블록은 추하다. 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞는다.

그래서 try/catch 블록을 별도 함수로 뽑아내는 편이 좋다.

	void method1() {
		try {//정상 동작
			something1();
			something2();
			something3();
		}catch (Exception e){
			//오류 처리 동작
		}
		
	}
    
    /**********************************/
    //try/catch 블록 뽑아내기
	void tryCatch(){
		try {
		method1();
		}catch (Exception e) {
			//오류 처리만 한다.
		}
	}
	
	void method1() throws Exception{
		//정상 동작만 한다.
		something1();
		something2();
		something3();
		
	}

 

오류 처리도 가지 작업이다.

, 함수는 가지일만 한다는 원칙에 부합해야 한다.

따라서 오류처리를 하는 함수는 오류처리만 해야한다.

 

 

 

오류 코드 대신 예외를 사용하자

예를 들어, Error enum 클래스가 있다고 하자, 이 열거형 클래스가 변경되는 순간

열거형 클래스를 사용하는 곳 모두 컴파일을 다시 해야 한다.

그래서 Error enum 클래스를 변경하기 어려워진다.

반면에 오류 코드 대신 예외를 사용하면 재컴파일/재배치 없이도 새 예외 클래스를 추가할 수 있다.

 

반복하지 마라

중복은 문제이다. 코드 양이불필요하게 늘어나며 수정하고 싶다면 중복된 부분을 전부수정해야 한다. 또한 조금씩 상이한 부분이 생기면 유지보수가 더 힘들어진다.

중복은 가장 악이다

많은 원칙과 기법이 중복을 없애거나 제어할 목적으로 나왔다.

객체지향에서는 코드 중복을 부모 클래스로 몰아 없앤다.

구조적 프로그래밍, AOP, COP(Component Oriented Programming) 중복을 제거하기 위한 전략이다.

소프트웨어 개발에서 지금까지 일어난 혁신은 소크 코드에서 중복을 제거하려는 지속적인 노력으로 보인다.

 

함수를 어떻게 짜죠?

글짓기와 비슷하다. 먼저 생각을 기록한 읽기 좋게 다듬는다.

함수도 처음에는 길고 복잡하다. 들여 쓰기 단계도단계도 많고 중복된 루프도 많다. 인수 목록도 아주 길다. 이름은 즉흥적이고 코드는 중복된다. 이렇게 서투른 코드를 빠짐없이 테스트하는 단위 테스트 케이스를 만든다.

그런 다음 코드를 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거하고, 메서드를 줄이고 순서를 바꾼다. 때로는 전체 클래스를 쪼개기도 한다. 모든 과정에도 항상 단위 테스트를 통과한다.

누구도 처음부터 짜는 코드란 없다.

 

 

'IT책, 강의 > 클린코드(Clean Code)' 카테고리의 다른 글

4장 주석 - 나쁜 주석  (1) 2022.10.01
4장 주석 - 좋은 주석  (0) 2022.09.28
3장 함수 - 1  (0) 2022.07.05
2장 의미 있는 이름  (0) 2022.07.01
1장 깨끗한 코드  (0) 2022.06.27

문제

 

https://school.programmers.co.kr/learn/courses/30/lessons/92334#

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

신입사원 무지는 게시판 불량 이용자를 신고하고 처리 결과를 메일로 발송하는 시스템을 개발하려 합니다. 무지가 개발하려는 시스템은 다음과 같습니다.

  • 각 유저는 한 번에 한 명의 유저를 신고할 수 있습니다.
    • 신고 횟수에 제한은 없습니다. 서로 다른 유저를 계속해서 신고할 수 있습니다.
    • 한 유저를 여러 번 신고할 수도 있지만, 동일한 유저에 대한 신고 횟수는 1회로 처리됩니다.
  • k번 이상 신고된 유저는 게시판 이용이 정지되며, 해당 유저를 신고한 모든 유저에게 정지 사실을 메일로 발송합니다.
    • 유저가 신고한 모든 내용을 취합하여 마지막에 한꺼번에 게시판 이용 정지를 시키면서 정지 메일을 발송합니다.

 

제한사항도 존재하는데 주로, 인자 유효성 부분이다.

그런데 제거해도 동작을 한다. 즉, 구현할 필요 없다.

 

 

 


코드

import static java.util.stream.Collectors.*;

import java.util.*;
import java.util.stream.Stream;


class Solution6 {
	public static void main(String[] args) {
		Solution6 s = new Solution6();
		int[] solution = s.solution(new String[] {"muzi", "frodo", "apeach", "neo"}
			, new String[] {"muzi frodo", "apeach frodo", "frodo neo", "muzi neo", "apeach muzi"}
			, 2);
		System.out.println(Arrays.toString(solution));
		
	}
	
	
	final int[] FAIL = new int[] {};
	//id_list 이용자 아이디 , 신고 정보 배열 report, 신고가정지로될 횟수
	public int[] solution(String[] id_list, String[] report, int k) {
		//알람을 몇번 받을지 저장할 맵, 순서가 중요하므로 Linked를 사용했다.
		Map<String, Integer> alramMap = new LinkedHashMap<>();
		//내가 누굴 신고했는지 저장할 맵, 순서가 중요하므로 Linked를 사용했다.
		Map<String, List<String>> reportedMap = new LinkedHashMap<>();
		
		//받은 인자에 맞게, 초기화
		Stream.<String>of(id_list).forEach(user -> {
			reportedMap.put(user, new ArrayList<String>());
			alramMap.put(user, 0);
		});
		
		//신고당한 횟수
		Stream.<String>of(report) // 신고 목록으로 스트림 생성
				.distinct() // 한 유저가 한 유저를 중복 신고한 경우 1번으로 처리
				.peek( _report -> System.out.println("신고 = "+_report ) )
				.map(_report ->{
					String[] users = _report.split(" ");
					//[0] 원고, [1] 피고
					reportedMap.get(users[0]).add(users[1]);//내 리스트에 피고 저장
					return users[1];})// 피고인으로 구성된 스트림
				.peek( badGuy -> System.out.println("신고 당한 유저 = "+badGuy +"\n" ))
				.collect(groupingBy(
							user->user, 
							LinkedHashMap<String, Integer>::new , 
							collectingAndThen(counting(), Long::intValue)))
				//내가 몇 번 신고 당했는지, 맵으로 리턴 key 내id, value 신고당한 횟수
				//신고당한 적 없다면 맵에 존재하지 않는다.
				.forEach((badUser, count)->{
					if(count>=k ) {// k 번 이상일 경우 계정 정지, 해당 사용자를 신고한 사람은 알림을 받는다.
						reportedMap.forEach( (user, badUsers)->{
							if(badUsers.contains(badUser)) {
								alramMap.put(user, alramMap.get(user)+1 );
							}
						});
					}
				});
		//맵을 int[]로 변환리턴
		return alramMap.values().stream().mapToInt(Integer::intValue).toArray();
	}
}

결과

신고 = muzi frodo
신고 당한 유저 = frodo

신고 = apeach frodo
신고 당한 유저 = frodo

신고 = frodo neo
신고 당한 유저 = neo

신고 = muzi neo
신고 당한 유저 = neo

신고 = apeach muzi
신고 당한 유저 = muzi

[2, 1, 1, 0]

입력 받은 순서대로 값을 리턴해야된다. 그래서 순서를 유지하는 맵을 사용했다.

'자료구조&알고리즘 > Level1' 카테고리의 다른 글

숫자 문자열과 영단어  (0) 2022.10.01
성격 유형 검사하기  (0) 2022.09.26
올바른 괄호  (1) 2022.09.23
크레인 인형뽑기 게임  (1) 2022.09.21
신규 아이디 추천  (0) 2022.09.18

문제

https://school.programmers.co.kr/learn/courses/30/lessons/12909

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

문제 설명

괄호가 바르게 짝지어졌다는 것은 '(' 문자로 열렸으면 반드시 짝지어서 ')' 문자로 닫혀야 한다는 뜻입니다. 예를 들어

  • "()()" 또는 "(())()" 는 올바른 괄호입니다.
  • ")()(" 또는 "(()(" 는 올바르지 않은 괄호입니다.

'(' 또는 ')' 로만 이루어진 문자열 s가 주어졌을 때, 문자열 s가 올바른 괄호이면 true를 return 하고, 올바르지 않은 괄호이면 false를 return 하는 solution 함수를 완성해 주세요.

제한사항
  • 문자열 s의 길이 : 100,000 이하의 자연수
  • 문자열 s는 '(' 또는 ')' 로만 이루어져 있습니다.

코드

import java.util.Stack;

class Solution {
	boolean solution(String s) {
		Stack<Character> stack = new Stack<Character>();
		try {
			for (char cha : s.toCharArray()) {
				if (cha == '(')
					stack.add(cha);
				else { //문자열은 '(' , ')' 밖에 없기 때문에 이 조건식
					stack.pop();
				}
			}
		} catch (Exception e) {
			return false; // 예외발생은 pop()에서 날 것 즉, 짝이 안맞는다.
		}
		return stack.empty() ? true : false;
	}
}

결과

정확성  테스트
테스트 1 〉	통과 (0.17ms, 76.7MB)
테스트 2 〉	통과 (0.20ms, 81.6MB)
테스트 3 〉	통과 (0.19ms, 79MB)
테스트 4 〉	통과 (0.22ms, 79MB)
테스트 5 〉	통과 (0.27ms, 76.4MB)
테스트 6 〉	통과 (0.14ms, 77.1MB)
테스트 7 〉	통과 (0.24ms, 78.2MB)
테스트 8 〉	통과 (0.20ms, 75.6MB)
테스트 9 〉	통과 (0.25ms, 72.8MB)
테스트 10 〉	통과 (0.25ms, 75MB)
테스트 11 〉	통과 (0.20ms, 72.4MB)
테스트 12 〉	통과 (0.22ms, 75.1MB)
테스트 13 〉	통과 (0.30ms, 75.5MB)
테스트 14 〉	통과 (0.24ms, 73.4MB)
테스트 15 〉	통과 (0.21ms, 75.4MB)
테스트 16 〉	통과 (0.22ms, 99.7MB)
테스트 17 〉	통과 (0.21ms, 74.8MB)
테스트 18 〉	통과 (0.25ms, 68MB)
효율성  테스트
테스트 1 〉	통과 (15.83ms, 53.8MB)
테스트 2 〉	통과 (14.89ms, 53.1MB)
채점 결과
정확성: 69.5
효율성: 30.5
합계: 100.0 / 100.0

 


예제에서 s 문자열은 '(', ')'만 존재한다고 했다. 

만약 여러 문자열이 섞여있다면 else if로 변경만 하면 될 것이다.

'자료구조&알고리즘 > Level1' 카테고리의 다른 글

성격 유형 검사하기  (0) 2022.09.26
신고 결과 받기  (0) 2022.09.24
크레인 인형뽑기 게임  (1) 2022.09.21
신규 아이디 추천  (0) 2022.09.18
성격 유형 검사하기  (0) 2022.09.17

문제

https://school.programmers.co.kr/learn/courses/30/lessons/42579

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

문제 설명

스트리밍 사이트에서 장르 별로 가장 많이 재생된 노래를 두 개씩 모아 베스트 앨범을 출시하려 합니다. 노래는 고유 번호로 구분하며, 노래를 수록하는 기준은 다음과 같습니다.

  1. 속한 노래가 많이 재생된 장르를 먼저 수록합니다.
  2. 장르 내에서 많이 재생된 노래를 먼저 수록합니다.
  3. 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다.

노래의 장르를 나타내는 문자열 배열 genres와 노래별 재생 횟수를 나타내는 정수 배열 plays가 주어질 때, 베스트 앨범에 들어갈 노래의 고유 번호를 순서대로 return 하도록 solution 함수를 완성하세요.

제한사항
  • genres[i]는 고유번호가 i인 노래의 장르입니다.
  • plays[i]는 고유번호가 i인 노래가 재생된 횟수입니다.
  • genres와 plays의 길이는 같으며, 이는 1 이상 10,000 이하입니다.
  • 장르 종류는 100개 미만입니다.
  • 장르에 속한 곡이 하나라면, 하나의 곡만 선택합니다.
  • 모든 장르는 재생된 횟수가 다릅니다.
입출력 예
genres  plays     return
["classic", "pop", "classic", "classic", "pop"] [500, 600, 150, 800, 2500] [4, 1, 3, 0]
입출력 예 설명

classic 장르는 1,450회 재생되었으며, classic 노래는 다음과 같습니다.

  • 고유 번호 3: 800회 재생
  • 고유 번호 0: 500회 재생
  • 고유 번호 2: 150회 재생

pop 장르는 3,100회 재생되었으며, pop 노래는 다음과 같습니다.

  • 고유 번호 4: 2,500회 재생
  • 고유 번호 1: 600회 재생

따라서 pop 장르의 [4, 1]번 노래를 먼저, classic 장르의 [3, 0]번 노래를 그다음에 수록합니다.

  • 장르 별로 가장 많이 재생된 노래를 최대 두 개까지 모아 베스트 앨범을 출시하므로 2번 노래는 수록되지 않습니다.

 


코드

import java.util.*;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

class Solution {
	static int i = 0;

	public int[] solution(String[] genres, int[] plays) {
		return IntStream.range(0, genres.length) // intStream
				        .mapToObj(i-> new TopGenrePlay(i, genres[i], plays[i])) // 기본형에서 참조형으로
				        .collect(groupingBy( TopGenrePlay::getGenre))			 // key는 genres[i] 객체 그대로 Map<String, List<TopGenrePlay>>
				        .entrySet().stream()	//   Stream<Entry<String, List<TopGenrePlay>>>
				        .sorted( (a,b)-> { // 스트림 안에서 총 Play집계 값으로 내림차순
					       	 int sum1 = a.getValue().stream().mapToInt(TopGenrePlay::getPlay).sum();
					       	 int sum2 = b.getValue().stream().mapToInt(TopGenrePlay::getPlay).sum();
					       	 return Integer.compare(sum2, sum1);}) //내림차순  
				        .map( entry -> { //<Entry<String, List<TopGenrePlay>>> 
					       	 List tgpList = entry.getValue().stream().sorted()//정렬은 객체에 구현된 Comparable에 의존한다.
							       			 		                 .limit(2)//정렬된 결과에서 2개만 가져온다.
							       			 		                 .map( TopGenrePlay::getId ).collect(toList());//식별값만 필요하다.
					       	 entry.setValue(tgpList);
					       	 return entry; } )
				        .flatMap( entry-> Stream.of(entry.getValue().toArray(new Integer[] {}))) // 하나의 스트림으로 만든다.
				        .mapToInt( Integer::intValue ) //기본형 스트림으로 IntStream
				        .toArray()
				        ;
	}

	public static void main(String[] args) {
		Solution s = new Solution();
		System.out.println(Arrays.toString(s.solution(
				new String[] { "classic", "pop", "classic", "classic", "pop", "trot", "trot", "trot", "pop" },
				new int[] { 1000, 600, 2000, 1000, 2500, 6000, 200, 200, 600 })));

	}

	class TopGenrePlay implements Comparable<TopGenrePlay> {
		int id;
		String genre;
		int play;

		public int getId() {
			return id;
		}

		public void setId(int id) {
			this.id = id;
		}

		public String getGenre() {
			return genre;
		}

		public void setGenre(String genre) {
			this.genre = genre;
		}

		public int getPlay() {
			return play;
		}

		public void setPlay(int play) {
			this.play = play;
		}

		public TopGenrePlay(int id, String genre, int play) {
			super();
			this.id = id;
			this.genre = genre;
			this.play = play;
		}
		@Override
		public String toString() {
			return "[id="+ id + ", genre=" + genre + ", play=" + play + "]";
		}
		@Override
		public int compareTo(TopGenrePlay o) {
			int result = Integer.compare(o.play, this.play);
			if (result == 0) {
				return Integer.compare(this.id, o.id);
			}
			return result;
		}
	}

}

결과

정확성  테스트
테스트 1 〉	통과 (17.71ms, 82.3MB)
테스트 2 〉	통과 (13.47ms, 77.6MB)
테스트 3 〉	통과 (15.14ms, 81.7MB)
테스트 4 〉	통과 (15.90ms, 73.1MB)
테스트 5 〉	통과 (9.80ms, 81.5MB)
테스트 6 〉	통과 (15.30ms, 73.7MB)
테스트 7 〉	통과 (10.73ms, 72.7MB)
테스트 8 〉	통과 (9.44ms, 73.7MB)
테스트 9 〉	통과 (9.91ms, 73.8MB)
테스트 10 〉	통과 (16.23ms, 80.8MB)
테스트 11 〉	통과 (8.91ms, 75MB)
테스트 12 〉	통과 (8.83ms, 75.1MB)
테스트 13 〉	통과 (14.77ms, 81.3MB)
테스트 14 〉	통과 (10.05ms, 78.5MB)
테스트 15 〉	통과 (8.19ms, 75.4MB)

 


퇴근하고 이틀 동안 계속 붙잡았다... 컬렉션으로만 해결하려 했던게 오히려 나에겐 독이였다.

될듯..?말듯...

그러다 문득, 내가 자바를 하는데 너무 함수형으로 생각한 게 아닌가 라는 생각이 들었다.

갑자기 Class를 활용할 생각을 안하고 컬렉션만 쓰려했는지...참...

그래도 이 교훈은 오래 남을 듯 하다.

 

누군가 봐서 조금이라도 도움이 됐으면하는 마음으로 중간중간에 데이터 형을 입력했다.

위 코드의 개략적인 구도는 play, id 순 정렬은 객체의 Comparable 구현에 의존한다.

이 정렬 전에 장르별 정렬은 스트림을 사용해 장르별 그룹화 후 

내부 List 반복으로 총 집계를 내어 정렬했다.

필요한 정렬정보는 모두 모였으므로, 데이터 가공으로 id 배열을 만들어 리턴했다.

 

 

 

문제

https://school.programmers.co.kr/learn/courses/30/lessons/64061

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

이 차원 배열로 이루어진 인형 뽑기 기계에서 인형을 뽑기

뽑은 인형은 내 보관함에 저장되며, 저장된 인형이 2 연속 같다면 제거한다.

해당 위치에 인형이 없다면 아무일도 하지 않는다.

 

제거된 인형 수를 리턴하도록 구현

 

[제한사항]

  • board 배열은 2차원 배열로 크기는 "5 x 5" 이상 "30 x 30" 이하입니다.
  • board의 각 칸에는 0 이상 100 이하인 정수가 담겨있습니다.
    • 0은 빈 칸을 나타냅니다.
    • 1 ~ 100의 각 숫자는 각기 다른 인형의 모양을 의미하며 같은 숫자는 같은 모양의 인형을 나타냅니다.
  • moves 배열의 크기는 1 이상 1,000 이하입니다.
  • moves 배열 각 원소들의 값은 1 이상이며 board 배열의 가로 크기 이하인 자연수입니다.

 


코드

import java.util.Deque;
import java.util.LinkedList;
//크레인 인형뽑기 게임
class Solution7 {
	/*편의상 변수로 둔 것*/
	static Deque<Integer> deque = new LinkedList<>();
	static int count = 0;
	static int move = 0;
	static int[][] board = new int[][] {{0,0,0,0,0}
										,{0,0,1,0,3}
										,{0,2,5,0,1}
										,{4,2,4,4,2}
										,{3,5,1,3,1}};
	static int[] moves = new int[] {1,5,3,5,1,2,1,4};
	
	/* 제출 시 콘솔 출력 부분 제거 or 주석 */
	public static void main(String[] args) {
		Solution7 s = new Solution7();
		s.solution(board, moves);
		
		System.out.println("제거된 인형 "+count);
	}
	
	
	public int solution(int[][] board, int[] moves) {
		
		print();
		for(int i=0;i<moves.length;i++) {
			System.out.println("인형뽑기 [" + moves[i] +"] 위치 " );
			get(moves[i]-1 );  // 인덱스 값 보정을 위해 -1
			System.out.println();
			print();
		}
		return count;
	}
	
	// 원하는 인형을 집는다.
	public void get(Integer select) {
		for (int hei = 0; hei< board.length ; hei++) {
			int value = board[hei][select] ;
			System.out.println("인형 찾기... board["+hei+"]["+select+"]"  + (value != 0 ? " 인형 찾음!!" : " 인형 없음"));
			if(value != 0) {
				board[hei][select] = 0;
				add(value);
				return;
			}
			
		}
	}
	
	// 마지막으로 저장한 값과 저장할 값이 같다면 저장하지 않는다.
	public boolean add(Integer num) {
		if(deque.size()!=0 && deque.peekLast().intValue() ==  num.intValue()) {
			System.out.println("제거 = "+deque.peekLast()+","+num);
			deque.pollLast(); // 요소를 꺼냄과 동시에 제거를 해준다.
			count += 2; // 제거한 수 만큼 증가시킨다.
			System.out.println("인형보관함 " + deque);
			return false;
		}
		deque.add(num);
		System.out.println("인형보관함 " + deque);
		return true;
	}
	
	/*출력용 메서드 */
	private void print() {
		for(int i=0;i<board.length;i++) {
			System.out.print("[");
			for(int j=0;j<board[i].length;j++) {
				System.out.print(board[i][j] +( j== board[i].length-1? "" : ", "));
			}
			System.out.println("]");
		}
	}
}

결과

[0, 0, 0, 0, 0]
[0, 0, 1, 0, 3]
[0, 2, 5, 0, 1]
[4, 2, 4, 4, 2]
[3, 5, 1, 3, 1]
인형뽑기 [1] 위치 
인형 찾기... board[0][0] 인형 없음
인형 찾기... board[1][0] 인형 없음
인형 찾기... board[2][0] 인형 없음
인형 찾기... board[3][0] 인형 찾음!!
인형보관함 [4]

[0, 0, 0, 0, 0]
[0, 0, 1, 0, 3]
[0, 2, 5, 0, 1]
[0, 2, 4, 4, 2]
[3, 5, 1, 3, 1]
인형뽑기 [5] 위치 
인형 찾기... board[0][4] 인형 없음
인형 찾기... board[1][4] 인형 찾음!!
인형보관함 [4, 3]

[0, 0, 0, 0, 0]
[0, 0, 1, 0, 0]
[0, 2, 5, 0, 1]
[0, 2, 4, 4, 2]
[3, 5, 1, 3, 1]
인형뽑기 [3] 위치 
인형 찾기... board[0][2] 인형 없음
인형 찾기... board[1][2] 인형 찾음!!
인형보관함 [4, 3, 1]

[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 2, 5, 0, 1]
[0, 2, 4, 4, 2]
[3, 5, 1, 3, 1]
인형뽑기 [5] 위치 
인형 찾기... board[0][4] 인형 없음
인형 찾기... board[1][4] 인형 없음
인형 찾기... board[2][4] 인형 찾음!!
제거 = 1,1
인형보관함 [4, 3]

[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 2, 5, 0, 0]
[0, 2, 4, 4, 2]
[3, 5, 1, 3, 1]
인형뽑기 [1] 위치 
인형 찾기... board[0][0] 인형 없음
인형 찾기... board[1][0] 인형 없음
인형 찾기... board[2][0] 인형 없음
인형 찾기... board[3][0] 인형 없음
인형 찾기... board[4][0] 인형 찾음!!
제거 = 3,3
인형보관함 [4]

[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 2, 5, 0, 0]
[0, 2, 4, 4, 2]
[0, 5, 1, 3, 1]
인형뽑기 [2] 위치 
인형 찾기... board[0][1] 인형 없음
인형 찾기... board[1][1] 인형 없음
인형 찾기... board[2][1] 인형 찾음!!
인형보관함 [4, 2]

[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 5, 0, 0]
[0, 2, 4, 4, 2]
[0, 5, 1, 3, 1]
인형뽑기 [1] 위치 
인형 찾기... board[0][0] 인형 없음
인형 찾기... board[1][0] 인형 없음
인형 찾기... board[2][0] 인형 없음
인형 찾기... board[3][0] 인형 없음
인형 찾기... board[4][0] 인형 없음

[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 5, 0, 0]
[0, 2, 4, 4, 2]
[0, 5, 1, 3, 1]
인형뽑기 [4] 위치 
인형 찾기... board[0][3] 인형 없음
인형 찾기... board[1][3] 인형 없음
인형 찾기... board[2][3] 인형 없음
인형 찾기... board[3][3] 인형 찾음!!
인형보관함 [4, 2, 4]

[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 5, 0, 0]
[0, 2, 4, 0, 2]
[0, 5, 1, 3, 1]
제거된 인형 4

 


 

'자료구조&알고리즘 > Level1' 카테고리의 다른 글

성격 유형 검사하기  (0) 2022.09.26
신고 결과 받기  (0) 2022.09.24
올바른 괄호  (1) 2022.09.23
신규 아이디 추천  (0) 2022.09.18
성격 유형 검사하기  (0) 2022.09.17

문제

 

https://school.programmers.co.kr/learn/courses/30/lessons/72410

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

1단계 new_id의 모든 대문자를 대응되는 소문자로 치환합니다.
2단계 new_id에서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거합니다.
3단계 new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환합니다.
4단계 new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거합니다.
5단계 new_id가 빈 문자열이라면, new_id에 "a"를 대입합니다.
6단계 new_id의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다.
     만약 제거 후 마침표(.)가 new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거합니다.
7단계 new_id의 길이가 2자 이하라면, new_id의 마지막 문자를 new_id의 길이가 3이 될 때까지 반복해서 끝에 붙입니다.

[입출력 예]

nonew_idresult

예1 "...!@BaT#*..y.abcdefghijklm" "bat.y.abcdefghi"
예2 "z-+.^." "z--"
예3 "=.=" "aaa"
예4 "123_.def" "123_.def"
예5 "abcdefghijklmn.p" "abcdefghijklmn"

코드

class Solution {
    public String solution(String new_id) {
    	new_id = new_id.toLowerCase(); //1단계
    	new_id = new_id.replaceAll("[^a-z0-9\\-_\\.]", "");//2단계
    	new_id = new_id.replaceAll("\\.{2,}", ".");//3단계
    	new_id = new_id.replaceAll("^\\.", "");//3단계
    	new_id = new_id.replaceAll("\\.$", "");//4단계
    	if(new_id.isEmpty()) new_id="a"; //5단계
    	if(new_id.length()>15) new_id = String.copyValueOf(new_id.toCharArray(), 0, 15);//6단계
    	new_id = new_id.replaceAll("\\.$", "");
    	for(;new_id.length()<3;) new_id= new_id.concat(new_id.charAt(new_id.length()-1)+"" );//7단계
        return new_id;
    }
}

결론

정규식 기초를 보는 문제 같다. 

내가 생각하는 이 문제에서 바라는 정규식 수준을 키워드로 나열하면,

(반복) 수량자,  콤마(.)의 활용, 문자 클래스 문법([ ] , 이 대괄호 속 문법은 약간 다름), 이스케이프(\), 앵커

이 정도 같다.

 

개인적으로는 이정도만 알고, 필요할 때 더 공부하는 것이 맞는 것 같다. 

 

 

 

자바에서 정규식은 이스케이프 문제를 이스케이프화하는 부분이 실수하기 좋은 부분 같다. 

정규식의 예약문자를 이스케이프 하기 위한 "\"는 자바에서도 예약 문자라 이스케이프 해줘야 한다.

"\" => "\\"

System.out.println("[^a-z0-9\\-_\\.]");
System.out.println("\\.{2,}");
System.out.println("^\\.");
System.out.println("\\.$");
System.out.println("\\.$");
---------------------------------------------
[^a-z0-9\-_\.]
\.{2,}
^\.
\.$
\.$

실질적으로 자바가 먼저 입력받은 문자열을 파싱한 후, 아래 출력 값을 정규식 인자로 사용할 것이다.

 

 

이 부분을 조금 편하게 하는 제 방식을 다음과 같다.

//오류 무시하고 작성
System.out.println("[^a-z0-9\-_\.]");
//빈공간에!!
System.out.println("");
// [^a-z0-9\-_\.] 를 복붙, 이클립스가 자동으로'\'=>'\\' 치환해줌
System.out.println("[^a-z0-9\\-_\\.]");

--------------------------------------------------
위 결과를 ctrl -H 로 일괄 치환하면, 위험할 수 있는 이유
//오류 무시하고 작성
System.out.println("[^a-z0-9\\-_\\.]");
//빈공간에!!
System.out.println("");
// [^a-z0-9\\-_\\.] 를 복붙
System.out.println("[^a-z0-9\\\\-_\\\\.]"); 다른 부분은 이렇게 될 수도 있다

 


 

'자료구조&알고리즘 > Level1' 카테고리의 다른 글

성격 유형 검사하기  (0) 2022.09.26
신고 결과 받기  (0) 2022.09.24
올바른 괄호  (1) 2022.09.23
크레인 인형뽑기 게임  (1) 2022.09.21
성격 유형 검사하기  (0) 2022.09.17

문제

import java.util.HashMap;
import java.util.Map;

class Solution {
	Map<Character, Integer> surveyMap = new HashMap<Character, Integer>();
	int[] scoreArr = {3,2,1,0,1,2,3};
	String[] survey;
	int[] choices;
	
	public String solution(String[] survey, int[] choices) {
    	this.survey = survey;
    	this.choices = choices;
    	
    	for(char key : "RTCFJMAN".toCharArray()) {
    		surveyMap.put(key, 0);
    	}
    	
    	for(int i=0; i<survey.length;i++) {
    		String tmp = survey[i];
    		char l = tmp.charAt(0);
    		char r = tmp.charAt(1);
			calculate(l,r,i);
    	}
    	
    	StringBuilder sb =new StringBuilder();
    	sb.append( sum('R','T')>=0?"R" : "T");
    	sb.append( sum('C','F')>=0?"C" : "F");
    	sb.append( sum('J','M')>=0?"J" : "M");
    	sb.append( sum('A','N')>=0?"A" : "N");

        return sb.toString();
    }
	private int sum(char l,char r) {
		return surveyMap.get(l) - surveyMap.get(r);
	}
	private void accumulateValue(char key, int value) {
		surveyMap.put(key, surveyMap.get(key)+value);
	}
	private void calculate(char l,char r, int i) {
		if(choices[i]<4) {
			accumulateValue(l, scoreArr[choices[i]-1]);
		}else {
			accumulateValue(r, scoreArr[choices[i]-1]);
		}
	}
}

 

 

'자료구조&알고리즘 > Level1' 카테고리의 다른 글

성격 유형 검사하기  (0) 2022.09.26
신고 결과 받기  (0) 2022.09.24
올바른 괄호  (1) 2022.09.23
크레인 인형뽑기 게임  (1) 2022.09.21
신규 아이디 추천  (0) 2022.09.18

 

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class MyFile {
	static int fileCount = 0;
	static int dirCount = 0;
	
	public static void main(String[] args) throws IOException {
    //폴더 경로
		String src = "C:\\Users\\kks\\Documents\\0000\\abc";
		String dst = "C:\\Users\\kks\\Documents\\0000\\def";
		String tmpDir = "C:\\Users\\kks\\Documents\\0000\\temp";
				
		if(new File(src).isDirectory()&& new File(tmpDir).isDirectory()) {
			fileMove(src, tmpDir, false);
			fileMove(tmpDir, dst, true);
		}
		System.out.println("총 파일 : "+ fileCount);
		System.out.println("총 폴더 : "+ dirCount);
		
	}
	
	private static void fileMove(String src, String dst , boolean srcDelete) throws IOException {
		File srcFile = new File(src);
		FileInputStream in = null;
		BufferedInputStream bis = null;
		
		FileOutputStream out = null;
		BufferedOutputStream bos = null;
		
		File[] files = srcFile.listFiles();
		for(File file : files) {
			File dstFile = new File(dst+"\\"+file.getName());
			if(srcDelete) {
            // JVM 종료 시 삭제 
				file.deleteOnExit();
			}
			
			if(file.isDirectory()) {
				dirCount++;
				dstFile.mkdir();
				fileMove(file.getAbsolutePath(), dst+"\\"+file.getName(), srcDelete);
				
			}else {
				fileCount++;
				in = new FileInputStream(file);
				bis = new BufferedInputStream(in);
				
				out = new FileOutputStream(dstFile);
				bos = new BufferedOutputStream(out);
				
				int data = 0;
				while ((data = bis.read())!= -1 ) {
					bos.write(data);
				}
				bos.close();
				bis.close();
			}
		}
	}
}

 

출발지 폴더, 임시 폴더, 목적지 폴더를 정해 파일을 옮기는 예제

폴더 내의 파일 중 폴더가 있으면 재귀 호출

'개발 > 자바(JAVA)' 카테고리의 다른 글

객체 지향 - 2  (0) 2023.01.27
객체 지향 - 1  (0) 2023.01.25
변수의 타입  (0) 2022.07.07
변수  (0) 2022.07.03
자바란?  (0) 2022.07.02

기본형 변수 타입

  • 문자형
    문자 : char 
  • 숫자
    정수 : byte, short, int, long
    실수 : float, double
  • 논리형
    boolean

총 8개

 

기본형과 참조형

기본형은 실제 값을 저장한다

8개가 존재한다.

성능상 이점 때문에 사용한다.

 

참조형은 어떤 데이터가 저장된주소 값을 저장한다.

기본형을 제외한 모든 것이다 참조형이다

배열, 클래스, 열거형 …

32bit JVM 쓰면 참조변수 크기는 32bit

64bit JVM 쓰면 참조변수 크기는 64bit이다.

 

 

기본형

정수형과 실수형이 존재 정수형의 대표는 int, 실수형의 대표는 double

종류\크기(byte) 1 2 4 8
논리형 boolean      
문자형   char    
정수형 byte short int(기본) long
실수형     float double(기본)

boolean 타입은 제어문이나 반복문에서 주로 사용된다.

char타입은 사실 아스키코드로 숫자에 문자가 매핑되어있다. 따라서 반복문도 가능하다

public class CharEx {
	public static void main(String[] args) {
		for(char a = 65; a < 91 ; a ++) {
			System.out.print(a+",");
		}
	}
}
// 결과 : A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,

숫자형 타입은 작은 데이터에서 큰 데이터로 자동 형변환이 된다. 

일부 예외도 있지만 크게 중요하지 않다.

 

'개발 > 자바(JAVA)' 카테고리의 다른 글

객체 지향 - 1  (0) 2023.01.25
파일 옮기기  (0) 2022.07.25
변수  (0) 2022.07.03
자바란?  (0) 2022.07.02
폴더, 파일 수 탐색  (0) 2022.05.27

+ Recent posts