문제

https://www.acmicpc.net/problem/9663

 

9663번: N-Queen

N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다. N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.

www.acmicpc.net

접근

2가지 방법이 있다: 

- 각 스퀘어를 전부 탐색하며 조건을 찾는다. => n^n 

- 한 행에 하나의 퀸이 온다는 것을 이용해 행별로 탐색 => n (풀이 참조)

 

2번째의 경우가 훨씬 시간복잡도가 줄기 때문에 두 번째 방법으로 간다

즉, 행이나 열 하나를 잡고 그 행에 첫 퀸을 두었을 때 다음 행이나 열을 돌면서 경우의 수를 탐색한다. 

 

풀이

const input = require("fs").readFileSync("/dev/stdin").toString();

const solution = (input) => {
  const N = +input;
  const row = Array(N).fill(0);
  let cnt = 0;

  const isPossible = (x) => {
    for (let i = 0; i < x; i++) {
      if (row[x] === row[i]) return false;
      if (x - i === Math.abs(row[x] - row[i])) return false;
    }
    return true;
  };

  const dfs = (x) => {
    if (x === N) {
      cnt++;
      return;
    }

    for (let y = 0; y < N; y++) {
      row[x] = y; 
      if (isPossible(x)) {
        dfs(x + 1); 
      }
    }
  };

  dfs(0);

  return cnt;
};

console.log(solution(input));

'Board > 알고리즘' 카테고리의 다른 글

백준 9251 LCS [JavaScript / Node.js]  (0) 2023.02.23
백준 1463 자바스크립트  (0) 2023.02.15
백준 15652 N과 M(4)  (0) 2023.02.05
백준 15651 N과 M(3)  (0) 2023.02.05
백준 15650 N과 M(2)  (0) 2023.02.05

문제

https://www.acmicpc.net/problem/15652

 

15652번: N과 M (4)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

접근

자연수 N과 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.

  • 1부터 N까지 자연수 중에서 M개를 고른 수열
  • 같은 수를 여러 번 골라도 된다.
  • 고른 수열은 비내림차순이어야 한다.

➡️ 어렵게 생각할 것 없이 중복을 허용하는 순서가 상관없는 경우의 수를 구하라는 것이다: 중복순열. 순열 문제였던 백준 15650 N과 N(2) 문제에서 중복체크하는 checked 배열만 없애주면 된다.

풀이

const input = require("fs")
  .readFileSync("/dev/stdin")
  .toString()
  .trim()
  .split(" ")
  .map(Number);

const [N, M] = input;
const output = [];
let result = "";

function dfs(level, start) {
  if (level === M) {
    result += `${output.join(" ")}\n`;
    return;
  }

  for (let i = start; i < N; i++) {
    output.push(i + 1);
    dfs(level + 1, i);
    output.pop();
  }
}

dfs(0, 0);

console.log(result);
 

'Board > 알고리즘' 카테고리의 다른 글

백준 1463 자바스크립트  (0) 2023.02.15
백준 9663 N-Queen  (0) 2023.02.08
백준 15651 N과 M(3)  (0) 2023.02.05
백준 15650 N과 M(2)  (0) 2023.02.05
백준 15649 N과 M(1)  (0) 2023.02.05

문제

https://www.acmicpc.net/problem/15651

 

15651번: N과 M (3)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

접근

백준 15651 N과 M(1) 과 같이 순열 문제이다.

단, 중복을 허용하기에 check 배열의 기능을 삭제해 주었다.

풀이

const input = require("fs")
  .readFileSync("/dev/stdin")
  .toString()
  .trim()
  .split(" ")
  .map(Number);

const [N, M] = input;
const output = [];
let result = "";
const checked = Array(N).fill(false);

function dfs(level, start) {

  if (level === M) {
    result += `${output.join(" ")}\n`;
    return;
  }
  
  for (let i = start; i < N; i++) {
    if (checked[i]) continue; // backtracking
    checked[i] = true;
    output.push(i + 1);
    dfs(level + 1, i);
    output.pop();
    checked[i] = false;
  }
}

dfs(0, 0);

console.log(result);

'Board > 알고리즘' 카테고리의 다른 글

백준 9663 N-Queen  (0) 2023.02.08
백준 15652 N과 M(4)  (0) 2023.02.05
백준 15650 N과 M(2)  (0) 2023.02.05
백준 15649 N과 M(1)  (0) 2023.02.05
백준 2606 자바스크립트  (0) 2023.01.26

문제

https://www.acmicpc.net/problem/15650

 

15650번: N과 M (2)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

접근

문제 15649 N과 M(1) 과 비교했을 때,

  • 고른 수열은 오름차순이어야 한다.

 

라는 조건이 추가됐다. 즉, [4,3] 의 경우 [3,4] 로 정렬되어 경우의 수 [4,3]와 [3,4]는 같은 것으로 간주된다.

 

N과 M (1)는 재귀를 위한 for loop에서 index를 항상 맨 처음부터 다시 돌았다면(모든 경우의 수를 위해), 이 문제에서는 지난 숫자는 제외하고 for loop을 진행한다(중복을 피하기 위해)

풀이

const input = require("fs")
  .readFileSync("/dev/stdin")
  .toString()
  .trim()
  .split(" ")
  .map(Number);

const [N, M] = input;
const output = [];
let result = "";
const checked = Array(N).fill(false);

function dfs(level, start) {

  if (level === M) {
    result += `${output.join(" ")}\n`;
    return;
  }

  for (let i = start; i < N; i++) {
    if (checked[i]) continue;
    
    checked[i] = true;
    output.push(i + 1);
    dfs(level + 1, i);
    output.pop();
    checked[i] = false;
  }
}

dfs(0, 0);

console.log(result);

'Board > 알고리즘' 카테고리의 다른 글

백준 15652 N과 M(4)  (0) 2023.02.05
백준 15651 N과 M(3)  (0) 2023.02.05
백준 15649 N과 M(1)  (0) 2023.02.05
백준 2606 자바스크립트  (0) 2023.01.26
백준 14888 자바스크립트  (0) 2023.01.22

[공부정리] 순열 vs 조합 vs 중복순열 vs 중복조합 (feat. 백준 N과 M 문제 시리즈)


문제

https://www.acmicpc.net/problem/15649

 

15649번: N과 M (1)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

접근

첫째 줄에 자연수 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)

➡️ 수의 범위가 작아 완전탐색 으로 접근

 

중복 없는 수열

➡️ 백트래킹

➡️ 수열 문제니까 조합(combination) 또는 순열(permutation) 인데, 중복이 없다고 했으므로 순열 문제.

풀이

const input = require("fs").readFileSync('/dev/stdin').toString().trim().split(" ").map(Number);

const [n, m] = input;
let result = "";
const output = [];
const checked = Array(n).fill(false);

function dfs(cnt) {
	
    if (cnt === m) {
        result += `${output.join(" ")}\n`;
        return;
    }

    for (let i = 0; i < n; i++) {
        if (checked[i] === true) continue;
        checked[i] = true;
        output.push(i + 1);
        dfs(cnt + 1);
        output.pop();
        checked[i] = false;
    }
}

dfs(0);

console.log(result);

 

'Board > 알고리즘' 카테고리의 다른 글

백준 15651 N과 M(3)  (0) 2023.02.05
백준 15650 N과 M(2)  (0) 2023.02.05
백준 2606 자바스크립트  (0) 2023.01.26
백준 14888 자바스크립트  (0) 2023.01.22
백준 2661 자바스크립트  (0) 2023.01.21

안녕하세요,

글쓰는 또라이,
일명 글또 8기를 시작하게 되었습니다.

 

글또 공식 노션 보러가기

글또 페이스북 보러가기

 

왜 글또를 시작했나?

개발을 공부한지 1년, 다른 분야보다도 기술 분야는 글로 하는 소통이 더 활발할 분야라는 인상을 받았다.

짧은 프로젝트 하나를 하더라도 노션을 통해 서로의 작업 현황을 공유하는 부분이 인상적이었고,

서로 다른 시간에 작업하더라도 언제든 확인할 수 있게끔 기록을 남겨두는 것이란 생각이 들었다.

- 또 개인적으로는 전문적인 기술 내용의 공유는 말로 하기 보다 잘 정돈된 글로 공유할 때 더 효과적이라고 생각한다.

마지막으로 프로젝트가 끝난 후에도 '회고'라는 글쓰기 활동을 통해 스스로와 팀원들에게 소통하는 시간을 가지는 점에서도

개발 분야에서 글쓰기란, 개인성장과 동료 간의 소통을 위해 필수적인 역량이란 생각이 들었다.

 

추가적으로 개인 브랜딩과 홍보 수단으로써 글의 힘이 크다.

벨로그라는 개발자들을 위한 블로그 플랫폼도 따로 있을 정도로...

어느 개발 모임에 가도 '파워블로거'가 가지는 영향력이 크다는 것도 꽤 인상적이었다.

이런 점들을 고려할 때 꾸준한 글쓰기를 통해 미리 글쓰기 역량을 키워놓아야겠다는 생각이 들었다. 

 

하지만 이런 생각을 하고 야심차게 시작한 블로그를 방치한지 어느덧 1년이 다되었다.

무엇이든 습관이 되지 않은 것을 습관으로 만드는 데는 꾸준함이 필요한 법인데, 그게 참 혼자서 하기가 쉽지 않다.

그래서 글또를 신청하게 되었다.

뭐든 함께 하면 꾸준히 하기 쉽기에...글을 쓰지 않으면 자극해줄 누군가가 필요했다.

 

 

글또는 이런 곳이었다

개발자들이 모여 2주에 한 번씩 글을 쓰고 피드백을 받을 수 있는 커뮤니티.

예치금 10만원을 넣고 글을 한 번씩 쓰지 않을 때마다 돈이 차감된다.

영리 목적 모임은 아니기 때문에 이렇게 걷힌 금액은 기부된다고...

글의 양식은 자유! 회고를 써도 좋고 기술 정보 나누는 글을 써도 좋다고 한다.

무엇보다 좋은 점은 글또콘 등 다른 개발분야의 분들과 활발히 네트워킹 할 수 있는 곳이 있어 

지속적으로 영감을 받을 수 있는 장이 있다는 것이 매력적이다.

 

지원부터 OT까지, '개발자'들의 글쓰는 커뮤니티라 개발 관련 활동 질문이 많을 거란 예상과 달리

"너는 누구냐"는 철학적인(?) 질문이 많았다는 것이 의외였다.

그도 그럴 것이  글또는 지원서에서부터 "삶의 지도"를 그려보는 항목이 있었는데,

내 삶을 돌아보며 현재 나를 만든 과거의 일련의 사건들을 나열하고 성찰을 적는 것이었다.

보통 기술적인 질문을 묻는 것이 아니라 새로웠다. 이 시간을 통해 기술에 집중된 글을 쓰는 것도 중요하지만,

계속해서 내가 지금 어디에서 무엇을 하고 있는지를 인지하고

거시적인 방향성을 잃지 않는 것이 훨씬 중요하다는 생각을 하게 되었다.

 

 

"나는 어떤 유형의 사람인가요? 어떤 유형의 사람과 만날 때 시너지가 나나요?"

 

반년간 4회정도 진행 예정이라는 커피드백 시간을 위한 설문조사에서 역시 "내가 어떤 사람인가"에 대한 질문이 주를 이뤘다.

그 중 "타인과의 관계 속의 나" 는 어떤 사람인지,를 돌아보게 해준 사진 속 질문이 인상 깊었다.

 

좋은 질문들 덕에 나에 대한 진솔한 성찰을 가질 수 있는 시간이었다.

앞으로는 자율적인 글쓰기 시간이 되기에, 위 질문들만큼 의미 있는 주제를 떠올릴 수 있을지 모르겠다.

그래도 글또가 단순 정보 전달보다 성찰을 통한 성장에 취지를 두고 만들어진 커뮤니티라는 걸 알 수 있었고,

이러한 모임에서의 교류를 통해 더 많은 영감을 받을 수 있으리라.

 

앞으로 글또에서

그렇게 시작한 2023 상반기, 글또와 함께하면서 계획한 일들을 회고하는 시간으로 가져가고자 한다.

 2023년 상반기 우선순위는 역시: 취업 준비, 그리고 방통대 학점 관리이다. 이에 대한 회고가 주를 이루는 주제가 될 것이다.

진솔한 성찰을 통해 배운 것을 소화하고 체화하고 싶다.

 

이를 위해 스스로를 위해 몇 가지 규칙을 세워보았다.

1. '매주 토요일 오전'은 글을 읽고 쓰는 시간으로 정한다.

2. 피드백을 활용한다. 내 머릿속에서 나오는 건 거기서 거기다.

매주 팀원들에게 피드백을 달고 받으며 피드백 시간을 활용하자.

3. 글쓰기가 다가 아니다. 적극적인 커피챗 참여를 통해 사람을 통해서도 영감 얻기.

4. 담백하게 쓴다. 쓸데없는 과장, 수사 x

5. 글쓰기 전 반드시 사실 확인을 한다.

6. 문체를 통일하여 일정한 문체를 유지한다.

7. 글은 올린 후에도 계속해서 퇴고한다.

8. 링크드인과 글또 슬랙을 이용해 다른 이들의 글을 읽는 시간을 가진다.

 

얼마나 지킬 수 있을지 모르겠지만 반 년간 한 번 열심히 해보자!

 

 

 

 

문제

https://www.acmicpc.net/problem/2606

 

2606번: 바이러스

첫째 줄에는 컴퓨터의 수가 주어진다. 컴퓨터의 수는 100 이하이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다. 둘째 줄에는 네트워크 상에서 직접 연결되어 있는 컴퓨터 쌍의 수가 주어

www.acmicpc.net

접근 

기본적인 그래프 탐색 문제. 

연결된 노드가 몇 개인지를 구하는.

 

단, 맨 처음 노드 시작을 인덱스 0이 아닌 1로 시작해줘야 한다.

풀이

const fs = require("fs");
const input = fs.readFileSync("./input.txt").toString().trim().split("\n");

function solution(input) {
  const N = +input.shift(); // 노드 수
  const M = +input.shift(); // 간선 수
  const edges = input.map((v) => v.split(" ").map(Number));

  // 그래프 그리기
  const vertices = Array(N + 1)
    .fill(0)
    .map(() => []);
  for (let edge of edges) {
    const [v0, v1] = edge;
    vertices[v0].push(v1);
    vertices[v1].push(v0);
  }

  // 실행
  const seen = new Set();
  let count = 0;
  const dfs = (start) => {
    for (let end of vertices[start]) {
      if (!seen.has(end)) {
        seen.add(end);
        count++;
        dfs(end);
      }
    }
  };
  seen.add(1);
  dfs(1);
  console.log(seen);
  return count;
}

console.log(solution(input));

참조: https://www.acmicpc.net/source/54379165

 

로그인

 

www.acmicpc.net

 

'Board > 알고리즘' 카테고리의 다른 글

백준 15650 N과 M(2)  (0) 2023.02.05
백준 15649 N과 M(1)  (0) 2023.02.05
백준 14888 자바스크립트  (0) 2023.01.22
백준 2661 자바스크립트  (0) 2023.01.21
백준 14889 자바스크립트  (0) 2023.01.19

문제

https://www.acmicpc.net/problem/14888

 

14888번: 연산자 끼워넣기

첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수, 

www.acmicpc.net

N개의 수로 이루어진 수열 A1, A2, ..., AN이 주어진다. 또, 수와 수 사이에 끼워넣을 수 있는 N-1개의 연산자가 주어진다. 연산자는 덧셈(+), 뺄셈(-), 곱셈(×), 나눗셈(÷)으로만 이루어져 있다.

 

식의 계산은 연산자 우선 순위를 무시하고 앞에서부터 진행해야 한다. 또, 나눗셈은 정수 나눗셈으로 몫만 취한다. 음수를 양수로 나눌 때는 C++14의 기준을 따른다. 즉, 양수로 바꾼 뒤 몫을 취하고, 그 몫을 음수로 바꾼 것과 같다. 이에 따라서, 위의 식 4개의 결과를 계산해보면 아래와 같다.

  • 1+2+3-4×5÷6 = 1
  • 1÷2+3+4-5×6 = 12
  • 1+2÷3×4-5+6 = 5
  • 1÷2×3-4+5+6 = 7

N개의 수N-1개의 연산자가 주어졌을 때, 만들 수 있는 식의 결과가 최대인 것과 최소인 것을 구하는 프로그램을 작성하시오.

 

접근

3    //N
3 4 5 // 수열
1 0 1 0 // 각각 +,-,*,/ 연산자의 갯수

이라면

만들 수 있는 식

3+4*5 = 35

3*4+5 = 17

 

- 첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 이므로 -> 완전탐색 

- 수열은 이미 주어졌으니 따로 구할 필요 x

- 연산자의 순서에 따라 값이 달라지니 순열(permutation) 문제겠구나.

-- 1. 들어온 수열과 연산자를 각각 다른 배열에 넣는다. 

-- 2. 수열.join("연산자")로 반복문을 돌린다.

--- 2-1.결과를 array 에 푸시한다. 

--- 2-2.연산자 배열의 위치를 바꾼다.

-- 3. 결과 배열에서 가장 작은 수와 가장 큰 수를 찾아 리턴한다.

 

1. 들어온 수열과 연산자를 각각 다른 배열에 넣는다. 

const input = require("fs")
  .readFileSync(address)
  .toString()
  .trim()
  .split("\n")
  .map((v) => v.split(" ").map(Number));

function getOperations(arr) {
  const operations = ["+", "-", "*", "/"];

  operations.forEach((v, idx) => {
    let amount = arr[idx];
    for (let j = 0; j < amount; j++) {
      operations.push(operations[idx]);
    }
  });

  return operations;
}

function solution(input) {
  const [N, A, amount] = input;
  const operations = getOperations(amount);

  return operations;
}

 

 2-1. 재귀를 이용해 결과를 array 에 푸시한다.

// 백준 제출시
// const address="/dev/stdin"

// VSC 테스트시
const address = "./input.txt";

const input = require("fs")
  .readFileSync(address)
  .toString()
  .trim()
  .split("\n")
  .map((v) => v.split(" ").map(Number));

const getOperations = (arr) => {
  const operations = ["+", "-", "*", "/"];

  operations.forEach((v, idx) => {
    let amount = arr[idx];
    for (let j = 0; j < amount; j++) {
      operations.push(operations[idx]);
    }
  });

  return operations;
};

function solution(input) {
  const [N, A, amount] = input;
  const operations = getOperations(amount);

  const results = [];
  let result = Number.MAX_SAFE_INTEGER;
  let check = Array(N).fill(0);

  const recursion = (idx) => {
    if (idx === N - 1) return;
    const copy = [...A];
    let tmpOp = operations.slice(0, 2);
    copy.splice(idx + 1, 0, tmpOp[0]);
    copy.splice(N, 0, tmpOp[1]);
    let tmp = operations.shift();
    operations.push(tmp);
    const test = eval(copy.join(""));
    results.push(test);
  };

  recursion(0);

  let min = Math.min(...results);
  let max = Math.max(...results);
  let answer = `${min}\n${max}`;

  return answer;
}

console.log(solution(input));

 

2-2.연산자 배열의 위치를 바꾼다.

const changeArrayOrder = function(list, targetIdx, moveValue) {
  // 배열값이 없는 경우 나가기
  if (list.length < 0) return;

  // 이동할 index 값을 변수에 선언
  const newPosition = targetIdx + moveValue;

  // 이동할 값이 0보다 작거나 최대값을 벗어나는 경우 종료
  if (newPosition < 0 || newPosition >= list.length) return;

  // 임의의 변수를 하나 만들고 배열 값 저장
  const tempList = JSON.parse(JSON.stringify(list));

  // 옮길 대상을 target 변수에 저장하기
  const target = tempList.splice(targetIdx, 1)[0];

  // 새로운 위치에 옮길 대상을 추가하기
  tempList.splice(newPosition, 0, target);
  return tempList;
};

 

풀이

시도 끝에 결국 실패. 여전히 재귀로 구현하는 것은 아직 어렵다.

// 백준 제출시
// const address="/dev/stdin"

// VSC 테스트시
const address = "./input.txt";

const input = require("fs")
  .readFileSync(address)
  .toString()
  .trim()
  .split("\n")
  .map((v) => v.split(" ").map(Number));

const getOperations = (arr) => {
  const operations = ["+", "-", "*", "/"];

  operations.forEach((v, idx) => {
    let amount = arr[idx];
    for (let j = 0; j < amount; j++) {
      operations.push(operations[idx]);
    }
  });

  return operations;
};

function solution(input) {
  const [N, A, amount] = input;
  const operations = getOperations(amount);

  const results = [];
  let result = Number.MAX_SAFE_INTEGER;
  let check = Array(N).fill(0);

  const recursion = (idx) => {
    if (idx === N - 1) return;
    const copy = [...A];
    let tmpOp = operations.slice(0, 2);
    copy.splice(idx + 1, 0, tmpOp[0]);
    copy.splice(N, 0, tmpOp[1]);
    let tmp = operations.shift();
    operations.push(tmp);
    const test = eval(copy.join(""));
    results.push(test);
  };

  recursion(0);

  let min = Math.min(...results);
  let max = Math.max(...results);
  let answer = `${min}\n${max}`;

  return answer;
}

console.log(solution(input));

 


참조풀이

https://gobae.tistory.com/49

https://junghyeonsu.tistory.com/196

 

'Board > 알고리즘' 카테고리의 다른 글

백준 15649 N과 M(1)  (0) 2023.02.05
백준 2606 자바스크립트  (0) 2023.01.26
백준 2661 자바스크립트  (0) 2023.01.21
백준 14889 자바스크립트  (0) 2023.01.19
[알고리즘 공부] 20230115 2주차 회고  (0) 2023.01.16

문제

https://www.acmicpc.net/problem/2661

 

2661번: 좋은수열

첫 번째 줄에 1, 2, 3으로만 이루어져 있는 길이가 N인 좋은 수열들 중에서 가장 작은 수를 나타내는 수열만 출력한다. 수열을 이루는 1, 2, 3들 사이에는 빈칸을 두지 않는다.

www.acmicpc.net

1,2,3 으로만 이루어진 N자리 숫자를 만든다. 이 중 가장 작은 수를 리턴한다.

단, 같은 순서가 연속해서 반복될 수 없다. ex) 33, 123123321

 

접근

- 12131213 처럼 딱히 규칙이 있지도 않고고, 1 <= N <= 80 으로 범위가 큰 수가 아니므로 완전탐색이 가능하다.

- 우선 수열이 좋은 수열인지(같은 순서가 연속해서 반복되지 않는지) 판단해야 한다. 해당 블로그를 참조하였다.

1211 => 길이 4, 1자리 비교, 나쁜 수열

1212 => 길이 4, 1자리 비교, 좋은 수열
1212 => 길이 4, 2자리 비교, 나쁜 수열

123123 => 길이 6, 1자리 비교, 좋은 수열
123123 => 길이 6, 2자리 비교, 좋은 수열
123123 => 길이 6, 3자리 비교, 나쁜 수열

- 해당 접근 방식을 코드로 짠다. 재귀를 이용해 전체 탐색을 했다.

 

 

 

시도

1차 시도

결과: 틀렸습니다

// 백준 제출시
// const address="/dev/stdin"

// VSC 테스트시
const address = "./input.txt";
const input = require("fs").readFileSync(address).toString().trim().split("\n");

const solution = (input) => {
  const N = +input;
  let stop = false;
  let min = Number.MAX_SAFE_INTEGER;

  const isGoodSequence = (str) => {
    let len = str.length;
    let loop = len / 2;
    let start = len - 1;
    let end = len;

    for (let i = 1; i <= loop; i++) {
      if (str.substring(start - i, end - i) === str.substring(start, end))
        return false;
      start -= 1;
    }

    return true;
  };

  const recursion = (len, str) => {
    if (stop) return;
    if (len === N) {
      stop = true;
      console.log(str);
      min = min > Number(str) ? Number(str) : min;
    }
    // 수열은 1,2,3 중 하나로 이루어져 있다.
    for (let i = 1; i <= 3; i++) {
      if (isGoodSequence(str + i)) recursion(len + 1, str + i);
    }
  };

  // 가장 작은 수를 구하는 것이므로 첫번째 자리는 항상 "1"이 되어야하고, 따라서 따로 탐색할 필요 x
  recursion(1, "1");

  return min;
};

console.log(solution(input));

 

2차 시도

결과: 맞았습니다!!

const address="/dev/stdin"
const input = require("fs").readFileSync(address).toString().trim().split("\n");

const solution = (input) => {
  const N = +input;
  let stop = false;

  const isGoodSequence = (str) => {
    let len = str.length;
    let loop = len / 2;
    let start = len - 1;
    let end = len;

    for (let i = 1; i <= loop; i++) {
      if (str.substring(start - i, end - i) === str.substring(start, end))
        return false;
      start -= 1;
    }

    return true;
  };

  const recursion = (len, str) => {
    if (stop) return;
    if (len === N) {
      stop = true;
      console.log(str);
    }
    // 수열은 1,2,3 중 하나로 이루어져 있다.
    for (let i = 1; i <= 3; i++) {
      if (isGoodSequence(str + i)) recursion(len + 1, str + i);
    }
  };

  // 가장 작은 수를 구하는 것이므로 첫번째 자리는 항상 "1"이 되어야하고, 따라서 따로 탐색할 필요 x
  recursion(1, "1");
};

solution(input);

 

 

분석

왜 min 부분을 삭제하니 통과한 걸까?

 우선 굳이 min 이 필요 없었다. 차례대로 작은수부터 대입하니까 반복문이 끝나기전 제일 먼저이자 마지막으로 찍힌 수가 어차피 가장 작은 수이기 때문이다.

 1차 풀이도 틀리지는 않았는데 틀렸던 이유는 정답(min)의 출력타입이 number 였기 때문이다. 정답으로 제출한 코드 마지막 부분만 console.log(Number(string)) 으로 쳤더니 틀렸다고 나온다. 출력 타입도 str 이어야 하는 구나.

 

나보다 속도가 적게 나온 다른 사람 코드 분석

1. 좋은 수열인지 판별하는 함수에서 substring 메소드가 아닌 slice 메서드를 사용

2. 재귀 함수에서 정답을 도출할 때 string 대신 array 를 사용

3. 재귀 함수에서 백트래킹시 조건문을 좋은 수열일 경우에만 진행이 아닌 나쁜 수열일 경우 반복문을 패스하도록 설정

const fs = require('fs');
// /dev/stdin
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n").map((v)=> v.split(" ").map((v)=> parseInt(v)))


// 숫자 1, 2, 3으로만 이루어지는 수열이 있다. 
// 임의의 길이의 인접한 두 개의 부분 수열이 동일한 것이 있으면, 
// 그 수열을 나쁜 수열이라고 부른다. 그렇지 않은 수열은 좋은 수열이다.
// 첫 번째 줄에 1, 2, 3으로만 이루어져 있는 길이가 N인 좋은 수열들 중에서 가장 작은 수를 나타내는 수열만 출력한다.

function solution (N){
    const arr = [1,2,3]
    const result = []
    const dfs = (depth, number = '') => {
        if(result.length > 0) return
        if (depth === N) {
            result.push(number)
            return
        }
        for(let i = 0; i < arr.length; i++) {
            const nextArray = number + arr[i].toString()
            if(isBad(nextArray)) continue
            dfs(depth+1, nextArray)
        }
        
    }

    dfs(0)



    return result[0]
}

function isBad (str) {
    for (let i = 1; i <= str.length/2; i++) {
        const last = str.slice(-i)
        const before = str.slice(-i*2, -i)
        if (last === before) return true
    }
    return false
}

console.log(solution(...input[0]))

 

 

 

'Board > 알고리즘' 카테고리의 다른 글

백준 15649 N과 M(1)  (0) 2023.02.05
백준 2606 자바스크립트  (0) 2023.01.26
백준 14888 자바스크립트  (0) 2023.01.22
백준 14889 자바스크립트  (0) 2023.01.19
[알고리즘 공부] 20230115 2주차 회고  (0) 2023.01.16

https://www.acmicpc.net/problem/14889

 

14889번: 스타트와 링크

예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.

www.acmicpc.net

 

문제 해석

주어진 n 에서 n/2 만큼 스타트팀의 경우의 수를 구한다. 스타트팀을 구하면 남은 인원을 링크팀에 배치한다.

각 팀의 능력치를 구해 합의 차가 가장 작은 경우의 수를 구한다. 

 

문제 접근

스타트팀 n 에서 n/2 를 순서에 상관없이 뽑는 조합의 문제이다. 

ex) n = 6일 때 [1,2,3] 로 스타트팀을 구성한다면, [1,2,3] 이나 [2,1,3]이나 [3,1,2] 나 순서에 상관 없이 팀의 능력치는 각 팀원들로 만들 수 있는 모든 부분 집합 (x,y) 쌍의 합, 즉, (1,2)+(2,1)+(2,3)+{3,2)+(1,3)+(3,1) 이 될 것이기 때문이다. 

 

문제 풀이 

시도 끝에 아래 두 가지 풀이 참조 및 비교 

풀이1 : 재귀 이용

 

출처 - https://gobae.tistory.com/50

 

 

풀이2 : 구현 이용

 

출처 - https://github.com/jlee0505/algorithm-study/pull/1/commits/df6a36b419ef225e0d9196fc7126a3e762a0d194

 

배운 것 

1. min 값을 각각 0, MIN_SAFE_INTEGER, MAX_SAFE_INTEGER 의 차이:

MIN_SAFE_INTEGER, MAX_SAFE_INTEGER 는 각각 -9007199254740991, +9007199254740991 을 의미한다.

아래 코드의 min 의 초기 할당을 각각 0, MIN_SAFE_INTEGER, MAX_SAFE_INTEGER 으로 했을 때의 차이를 생각해보자.

 
min = Math.min(min, Math.abs(sSum - lSum));

 

2.  재귀로 풀 때와 구현으로 풀 때의 장단점:

첫 번째 제출은 풀이1(재귀)의 결과, 두 번째 제출은 풀이 2(구현)의 결과이다. 구현으로 풀면 가독성이 올라가고 직관적 이해가 쉽다는 장점이 있지만, 재귀로 푸는 것이 시간,공간 복잡도가 압도적으로 높다.


재귀는 여전히 어렵다.

 

'Board > 알고리즘' 카테고리의 다른 글

백준 15649 N과 M(1)  (0) 2023.02.05
백준 2606 자바스크립트  (0) 2023.01.26
백준 14888 자바스크립트  (0) 2023.01.22
백준 2661 자바스크립트  (0) 2023.01.21
[알고리즘 공부] 20230115 2주차 회고  (0) 2023.01.16

나의 Notion - 알고리즘

2023년부터는 인턴과 기업 코딩테스트로 공부 방향을 잡았다. 어딜 가도 1차 관문이 되는 코딩테스트가 중요한 기회 때 발목 잡는 걸 원치 않았다. 

 

알고리즘 공부 시작, 지난 2주 돌아보기

1월 알고리즘 학습 계획표

 

 

2023 목표를 인턴과 기업 코딩테스트로 잡고, 어딜 가서든 1차 관문이 되는 코딩테스트의 ‘기본’을 1,2월안에 마무리 하기로 했다. 3,4월에는 본격적으로 기출을 풀며 여름방학/하반기 인턴 코테 지원(4~5월 지원)을 준비하고 싶었기 때문이다. 그리하여 2월까지 세운 러프한 계획은 이러했다:

  • 1월: 강의와 함께 빈출 알고리즘 핵심은 제대로 익혀두기(이코테 2021)
  • 2월: 백준 추천 50제 풀며 1월 배운 내용 복습 + 보강
  • 3월: 우테캠, 카카오, 토스 등 국내 기업 코딩테스트의 흐름을 주도하는 기업의 기출문제 풀어보기
  • 4월: 2차 관문인 과제 코테 준비 시작

 

 

 우선 이코테 2021 강의를 완강 + 80% 이해 및 체화를 목표로 1월 계획을 세웠다. 나는 정말 노베이스 상태에서 시작했기 때문에 전반적으로 어떻게 흘러가고 어떤 것들이 있는지 직접 한 번 훑고 싶었다. 지난 2주는 그런 시간이었다.

 

 그리디, 완전 탐색, 그리고 그래프 탐색(dfs, bfs)였다. dfs,bfs 는 중요하다는 말을 하도 많이 들어 감이 안잡혔지만 최대한 이해하고 넘어가려 했다. (관련 노션 보러가기)

 

 1월은 1회차기 때문에 문제를 많이 풀어보는 것보다 개념을 확실히 이해하는 게 더 중요하다고 생각했다. (이코테 기본 예제(2), 백준(1) - dfs, node 대신 edge 가 주어진 경우, 프로그래머스(5) - 스킵 )

 

 

Keep, Try, Problem

 회고를 쓰며 찾아보니 판교로 가는 길에서는 1주일 한 개념씩, 2~3문제를 풀어볼 것을 권한다. 예정보다 3일을 더 투자하여 (5,6,7일) 예제를 확실히 이해하고 추가로 백준 문제를 풀었다. 더 많은 문제를 풀었으면 좋았겠다는 아쉬움이 어쩔 수 없이 남는다. 하지만 카페 글을 참조해보니 오히려 이정도가 딱 맞았다는 생각이 든다. 외려 1주에 2개념을 잡았던 초기 계획이 불필요하게 빡세다는 느낌도 든다. 이에 따라

 

Keep 

- 무조건 문제 풀이보다 중요한 건 개념 이해

Problem

- 딱히 문제는 아니었지만... 한 개념 한 주씩 파는 것과 어떻게 다를지 다른 사람의 조언도 반영해볼 것.

- 예상했던 것보다 느슨해진다는 것 

Try 

- 한 개념 한 주씩: 두 개념 한 주씩 하던 지난 주와 후에 비교해볼 것

- 한 달 스프린트로 스터디를 개설하여 함께 뛰는 이가 있을 때 어떻게 달라질 수 있는지 비교해볼 것

- 불안요소를 글로써 가시화하고 제거할 수 있는 대책을 세우자.

 

다음 스텝은?

1. 한 개념 한 주씩 양치기 + 체화

다음 주부터는 본격적으로 양치기에 들어가려 한다. 지난 2주가 이코테 강의 위주의 전체 흐름 잡기 + 개념 익히기였다면, 다음 5주는 백준50제 위주의 양치기 체화 과정이 목표이다.

 

5주차 계획

 

다음 스텝: 2. 스터디와 함께 풀기

스터디를 개설했다.

 

이유:

  1. 해당 카페 포함 많은 이들이 같이 가는 스터디원이 있는 것을 강력 추천
  2. 지인과 네트워킹을 통해 혼자서는 몰랐을 시각/정보/인적자원 습득의 가능성
  3. 반복 문제 풀이인만큼 늘어지지 않게 함께 가는 메이트 만들기

걱정되는 부분:

지인과 함께 시작한 스터디라 아래 부분이 걱정되었다.

  1. 너무 루즈해지진 않을까?
  2. 친목 위주로 가서 집중력과 학습 효과가 떨어지면 어떡하지? 

어떻게 해결할 수 있을까?:

  1. 나의 불안요소: 쉽게 distracted 된다.
    1. 목표를 확실히 한다: 이 4주간 나는 무엇을 얻고자 하는가?
      1. 실버1,2 or 골드 4 를 달성 (모든 문제를 이해하고 풀이했다는 전제하)
  2. 상대에 대한 불안요소: 평소 함께 잘 놀던 지인이다.
    1. 주의사항을 상기한다:
      1. ‘성의 없는 공부’에 절대 관대하지 말 것.
    2. 전반적인 주차별 목표&계획과 구체적인 실행 컨텐츠 시간 장소 등을 확실히 하자.

다음 텀도 화이팅

 

'Board > 알고리즘' 카테고리의 다른 글

백준 15649 N과 M(1)  (0) 2023.02.05
백준 2606 자바스크립트  (0) 2023.01.26
백준 14888 자바스크립트  (0) 2023.01.22
백준 2661 자바스크립트  (0) 2023.01.21
백준 14889 자바스크립트  (0) 2023.01.19

 

 

FECrash 함수형 프로그래밍 스터디 1부가 끝이 났다


오늘로 <쏙쏙 들어오는 함수형 코딩>책과 함께한 1부가 끝났다. 

카카오 엔터프라이즈의 테오가 강의하고, 넥스트 유니콘 파랑이 주최한 스터디.

강의 2~30분, 팀별활동 1시간 30분, 총 2시간씩

매주 목요일 저녁 진행되었다. -> 깃헙 보러가기

 

모두가 알찬 내용을 기대하고 온 만큼 열정적으로 참여하셨다.

예상 스터디 기간은 8~10주로, 1부와2부로 나뉘어 진행된다고 한다.

1부함수형 프로그래밍의 개념 및 필요성, 기초 활용법에 대해 팀으로 예제를 풀며 익히는 식으로 진행되었다.

2부부터는 쿼리 등 함수형 프로그래밍을 위해 더 함수를 잘 분리하는 심화 방법을 배운다고 한다.

매주 리팩토링 과제가 진행되며, 마지막에는 라이브러리를 만들어 보는 것으로 마무리 된다고 한다.

 

오늘(1/5, 2023)을 기준으로 1부가 끝이 났다.

지난 5주간 무엇을 배웠는지 돌아보고자 중간회고를 남기고자 한다.

 

테오가 강의하고 파랑이 리딩하는 함수형 프로그래밍 스터디

 

 

함수형 프로그래밍은 무엇이고,
우리는 왜 배워야 하는가.


 

자바스크립트는 사실 함수형 프로그래밍 언어이다.

 

"정확히는 우리가 아는대로 멀티 패러다임 언어입니다 ... javascript를 창시한 Brendan Erich는 언어를 개발할 당시 유행하던 객체지향에 한계를 느끼고 LISP, scheme등 함수형 프로그래밍에 관심을 가지고 있었기에 함수형 프로그래밍의 형태로 언어를 만들고 싶어 했습니다. 하지만 Netscape의 그의 상사는 당시 개발자들이 제일 많이 쓰던 Java와 같은 문법으로 만들기 요구했기 때문에 결국 둘의 혼종의 형태로 세상에 나오게 되었습니다. :) 결국 javascript에는 언어의 태생부터 함수형 프로그래밍의 개념들이 녹아있고 동시에 객체지향의 가치는 다소 희석이 되어 있는 형태의 언어였습니다."

출처 - 테오가 쓴 글

 

 

글에는 나오지 않았지만 강의에서 테오는 자바스크립트ES6에 추가된

this가 없는 "화살표 함수" 등은 함수형 프로그래밍에 적합한 문법이라고 할 수 있다고 덧붙이셨다.

 

결국 우리는 javascript를 쓸 수 밖에 없기에 객체지향이냐 함수형이냐 패러다임을 선택해서 깊게 파야 하는 것이 아니라

javascript 그 자체를 잘 하기 위해서 javascript의 함수형과 객체지향을 둘 다 알아야 하는 것이다.

 

 

그래서
함수형 프로그래밍이
뭔데?

함수형 프로그래밍은 결국 "범위(scope)"를 기준으로 한 패러다임 중 하나이다.

 

지역 변수 사용은 나쁘다고 한다.

여러 파일에서 참조 및 변경할 수 있어 에러 발생률이 높이기 때문이다.

그래서 그 범위를 "클래스"로 한정해 사용하자, 는 것이 "객체 지향 프로그래밍",

이 범위를 더 좁혀 "함수"로 한정해 사용하자, 는 것이 "함수형 프로그래밍"이다.

 

함수로 범위를 좁힌 변수(지역 변수)를 사용하며,

이러한 함수들의 집합으로 프로그래밍을 완성하는 것,

그것이 함수형 프로그래밍이라 할 수 있다.

 

함수형 프로그래밍을 할 때 주의해야 할 점은,

이러한 전체 프로그래밍을 이루는 작은 구성원인 함수

가능한 작고, 테스트가 쉬우며, 분리와 재조립이 쉬운 형태로 만드는 것이다.

 

이러한 함수를 만드는 법

바로 함수(범위) 밖의 요소에 영향을 받지도 주지도 않는,

즉, "순수함수"로 만드는 것이다.

 

그리고 책에서는 함수를 "순수함수"로 만드는 방법에 대해 알기 쉽게 소개한다.

 

 

 

순수함수가 핵심이다

 

 

순수하지 않은 함수, 즉 외부에 영향을 받거나 주는 테스트가 어려운 함수를

"순수함수"라고 한다고 했다.

하지만 순수함수를 어떻게 만드는데? 라는 질문에는

선뜻 대답하기 쉽지 않은데 그 방법이 다양할 수 있기 때문이라 생각한다.

 

하지만 이 책에서는 한 가지 방식을 제안하는데,

바로 바뀌는 변수는 오직 인자로만 받고(명시적 입력), 전역 변수나 dom 등 다른 범위의 변수는 일정 변경하지 않는 것이다.

이 과정에서 변경하는 데이터는 복사를 이용한다.

또한 그렇게 함수내에서 변경된 자료는 return 으로 출력한다(명시적 출력).

 

보다 구체적으로 알아보자.

저자는 바꾸는 법을 전보다 구체적으로 전달하기 위해

함수를 역할에 따라 액션, 계산, 데이터로 3가지로 분류한다.

책에서 말하는 액션, 계산, 데이터란 아래와 같다:

 

 액션  - 외부에 영향을 받거나 주는 함수(즉, 비순수 함수
 계산 - 외부에 영향을 받거나 주지 않는 함수(순수 함수) + 명시적인 입출력
 데이터 - 그냥 데이터 

 

결국 좋은 함수형 프로그래밍이란

비순수함수(액션)을 최대한 없애고,

순수함수(계산)을 최대한으로 만들어

테스트와 유지보수가 쉬운 코드를 만드는 것이다.

 

또 저자는 이 과정을 보다 직관적으로 설명하기 위해

명시적 입/출력, 암묵적 입/출력의 개념을 사용한다.

 

 명시적 입력 - 인자(parameter) 

 암묵적 입력 - 인자 외의 모든 입력 (ex. 지역 변수 사용) 

 명시적 출력 - return 

 암묵적 출력 - return 외의 모든 출력 (ex. alert ) 

 

명시적 입출력은 필요하다

1. 테스트를 쉽게 할 뿐 아니라,

2. 테스트가 없는 경우에도 개발자에게 예측가성성이 높은 코드를 주기 때문이다. 

 

구체적인 방법은 간단하다:

1. 계산 코드를 찾아 빼낸다.

2. 새 함수에 암묵적 입력과 출력을 찾는다.

3. 암묵적 입력은 인자로 암묵적 출력은 리턴값으로 바꾼다.

 

예시는 아래와 같다.

 

한 쇼핑몰에서 쇼핑 카트 안의 아이템들에 따라 세금을 추가하는 아래와 같은 코드가 있다.

function update_tax_dom () {
	set_tax_dom(shopping_cart_total * 0.10);
}

이 코드를 테스트 함수를 짠다고 생각해보라. 힘들다.

이 함수는 shopping_cart_total 이라는 외부 변수(지역 변수)를 건드리는 액션(비순수함수)이기 때문이다.

위의 코드에서 테스트 코드를 짤 수 있는 순수함수는 0개다.

 

이 중에서 계산(순수함수)로 나눌 수 있는 방법은 없는지 고민해보면 아래와 같다.

// 액션 - 외부데이터(dom 등)을 변경한다.
function update_tax_dom() {
	set_tax_dom(calc_tax(shopping_cart_total));
}

//계산 - 외부데이터를 일절 건드리지 않는다.(순수함수)
function calc_tax (amount) {
	return amount * 0.10;
}

이렇게 분리하면 calc_tax 는 순수함수이므로 테스트 코드를 짜기 쉽다.

이제 테스트 코드를 짤 수 있는 순수함수가 1개이다.

(순수함수를 포함하고 있지만 dom도 건드리는 비순수함수도 포함하고 있는 update_tax_dom 은 비순수함수이다.

비순수 함수를 하나라도 포함하는 함수는 비순수함수이다.)

 

이처럼 기존 테스트가 어려운(지역변수를 건드리고, dom을 건드리는) 함수를 테스트가 쉬운 함수(인자로 받아온 것만 변경, 명확한 결과값 return) 로 바꾸는 것. 이것이 1부 동안 리팩토링을 하며 배운 함수형 프로그래밍의 기초이다.

 

다시 한 번 말하지만, 액션(비순수함수)의 사용은 불가피하다.

프로그래밍의 목적은 데이터를 가공해 새로운 데이터를 만들어내는 데에 있기 때문이다.

그러나 유지보수가 쉽고 테스트가 쉬운 코드를 위해서는

이렇게 외부 데이터를 건드리는 함수인 액션의 사용을 '최소한'으로 줄이고, 

가능한 순수함수들로 채워넣는 데에 그 목적이 있다. 

 

 

데이터의 불변성

 

카피-온-라이트 - 데이터 변경 시 반드시 복사한다는 원칙

 

데이터를 바꾸지 않고 불러오기만 하는 것을 읽기(read)라고 한다.

데이터를 변경 하는 것을 쓰기(write)이라고 한다. 

카피-온-라이트(copy-on-write)는 이러한 데이터 변경 시 반드시 복사를 하여,

원본은 유지하고 복사본을 변경하라는 원칙이다.

이렇게 함으로써 원본 데이터는 불변성을 잃지 않을 수 있다.

 

데이터의 불변성을 지키는 것은 중요하다고 모두가 말한다.

불변성을 지키지 않는다면 사용할 데이터가 어디서 어떻게 바뀌어가는지 흐름을 쫓아가기 어렵고,

이는 곧 예기치 못한 side effects나 버그로 이어지게 만들기 때문이다.

 

이러한 불변성을 지켜주는 카피-온-라이트 방식을 사용하는 법은 간단하다:

 

1. 복사본 만들기

2. 복사본 변경하기(write)

3. 복사본 리턴하기

 

예시와 함께 보자.

아래는 메일링 리스트에 연락처를 추가하는 코드이며,

이메일 주소를 전역변수인 리스트에 추가한다.

const mailing_list = [];

const add_contact = (email) => {
	mailing_list.push(email);
}

const submit_form_handler = (event) => {
	const form = event.target;
    const email = form.elements["email"].value;
    add_contact(email);
}

전역 변수(외부 데이터)를 변경하는 것은

데이터 불변성에 어긋난다.

카피-온-라이트 방식을 이용해 불변성을 지켜보자.

const mailing_list = [];

const add_contact = (email) => {
	const list_copy = [...mailing_list]; // 1. 복사한다.
	list_copy.push(email); // 2. 복사본을 원하는만큼 변경한다.
    return list_copy;		// 3. 복사본을 리턴한다.
}

const submit_form_handler = (event) => {
	const form = event.target;
    const email = form.elements["email"].value;
    add_contact(email);
}

이제 전역 변수 mailing_list 의 원본은 변경되지 않는다.

 

+

"복사를 하면 비용이 많이 들지 않나요?" 라는 반박에 저자는 이렇게 대처한다. 

카피-온-라이트에서 사용하는 복사는 얕은 복사(shallow copy)이기 때문에 생각보다 비용이 많이 들지 않는다고.

이는 복사를 하지 않아 데이터의 불변성을 지키지 못할 경우 발생가능한 오류를 수정하는 비용보다 적을 것이라고 말이다.

하지만 데이터를 변경할 지도 모르는 믿을 수 없는 외부 함수(ex.남이 짠 레거시 코드)를 사용해야만 한다면

조금 비싸더라도 깊은 복사(deep copy)를 사용할 것을 저자는 권한다.

* 얕은 복사 vs 깊은 복사

 

솔직히 이 부분에 대해서는 잘 이해가 안간다. 

신뢰할 수 없는 코드가 참조를 가지고 있으면 안되기 때문에 참조를 쓰지 않고 전체를 전부 복사하는 깊은 복사를 쓰는 것이라고 한다. 

일단은

1. 자스에서는 lodash 를 이용해 깊은 복사 사용하면 편하며,

2. 신뢰할 수 없는 코드로 들어가는/오는 데이터는 나갈때,들어올 때 모두 깊은 복사로 감싸주어야 한다.

만 그렇구나~ 하고 받아들이기로 했다.

 

 

계층의 분리

 

계층이란 결국 추상화의 정도이다.

목적이나 추상의 정도가 같은 함수들을 같은 계층이라고 본다.

 

즉,

같은 계층 = 

1) 같은 목적

2) 같은 구체화 수준

라고 이해했다.

 

계층을 나누는 데에 정답은 없다.

각자의 기준과 이유가 분명하면 된다.

 

계층을 나누는 감각을 기르다보면

코드를 설계하는 감각을 기를 수 있다고 한다.

 

아래는 우리팀이 진행한 계층의 분리이다.

이를 바탕으로 최종 리팩토링을 진행하였다.

 

 

6팀

 

 

 

 

회고 글을 쓰면서,

1. 헷갈리던 함수형 프로그래밍의 개념에 대해 제대로 이해할 수 있었다.

2. 내가 무엇을 모르는지(깊은 복사가 필요한 이유)를 확실히 할 수 있었다. 

3. 계층형 설계 개념을 대충 읽었는데 다시 한 번 복습할 수 있었다. 

4. 못다한 리팩토링을 회고를 쓰면서 마무리하였다.

5. 중간에 정리하는 시간을 가지며, 복습 및 정비하는 시간을 가질 수 있었다.

 

'Extracurricular > 활동' 카테고리의 다른 글

[Next.js] 번역 컨트리뷰터  (0) 2024.03.25
새싹톤 우수상 회고  (0) 2024.03.25

+ Recent posts