Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions JavaScript/07주차/assignment/재영/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## HOW TO CHECK

루트 디렉토리를 기준으로 다음을 순차적으로 입력해주세요.

### 현재 과제 디렉토리로 이동

```bash
cd JavaScript
cd 07주차
cd assignment
cd 재영
```

### 커맨드 실행

```bash
npm test
```

`pnpm`을 쓰신다면 `pnpm`으로 테스트 가능합니다.
```bash
pnpm test
```

`yarn`을 쓰신다면 `yarn`으로 테스트 가능합니다.
```bash
yarn test
```

### 테스트 코드 작성

`test.js`에서 테스트가 가능합니다.

#### 작성 방법

`expect`가 같은 것임을 확인하는 함수라면, `describe`는 일련의 `expect`를 단위로 묶어주는 grouping을 위한 함수입니다.

`describe`에 첫 번째 인자는 `"테스트 그룹명"`을, 두 번째 인자에는 `expect들의 배열`을 넣어주세요.
127 changes: 127 additions & 0 deletions JavaScript/07주차/assignment/재영/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// [x] 양방향 연결리스트는 각 노드의 prev와 next로 이동할 수 있어야 합니다.
class Node {
constructor(val, prev = null, next = null) {
this.val = val;
this.prev = prev;
this.next = next;
}
}

export class DoubleLinkedList {
constructor(val) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constructor 인자로 val을 넣어준 이유가 궁금합니다!🙋🏻‍♀️ 테스트 코드에서 보면 insert로 값을 추가하는 것 같은데 val 값을 인자로 넘겨주어 인스턴스를 생성하는 경우도 고려하신건가요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 맞습니다 :)
사용자가 처음부터 초기값을 넣어줄 수도 있고, 넣지 않을 수도 있다고 생각했어요.
유동적으로 입맛에 맞게 쓸 수 있도록 했다고 보시면 될 것 같아요 :)

this.node = val ? new Node(val) : null;
this.head = val ? this.node : null;
this.tail = val ? this.node : null;

this.cnt = val ? 1 : 0;
}

get prevNode() {
return this.node.prev ?? this.node;
}

get nextNode() {
return this.node.next ?? this.node;
}

// [x] insert메서드를 통해 특정 인덱스의 노드에 삽입할 수 있어야 해요.
insert(val) {
if (!this.cnt) {
const nowNode = new Node(val);
this.node = nowNode;
this.head = nowNode;
this.tail = nowNode;
} else {
const nowNode = new Node(val, this.node, this.node.next ?? this.head);
this.nextNode.prev = nowNode;
this.node.next = nowNode;
this.node = nowNode;

if (this.node.next === this.head) this.tail = nowNode;
}

this.cnt += 1;

return this;
}

// [x] get를 통해 원하는 인덱스의 노드를 가져올 수 있어야 해요.
getNthNodeFromHead(order) {
let i = 0;
let now = this.head;
while (i < order) {
i += 1;
now = now.next;
}

return now;
}

getNthNode(order) {
let i = 0;
let now = this.node;
while (i < order) {
i += 1;
now = now.next;
}

return now;
}

get nowNode() {
return this.node;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

접근자 getter를 사용하신 이유가 궁금합니다! doubleLinkedList.node를 해주어도 node의 값을 알 수 있지 않을까요??

Copy link
Collaborator Author

@JengYoung JengYoung Dec 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setter을 지정해주지 않았으므로 바깥의 로직이 클래스 내부의 상태를 조작할 수 없도록 안전하게 관리할 수 있기 때문입니다.
혹여나 바깥의 사용자가 로직을 실수로 변경하더라도, 안전하게 노드의 데이터를 보존할 수 있기 때문에 getter가 바람직하다고 생각해요!

아래는 도움이 되는 예시입니다.

class A {
    constructor() { this.node = 1 }
    get Node() {
        return this.Node
    }
}

const a = new A();
a.node = 3;
console.log(a.node) // 3

a.Node = 5;
console.log(a.node) // 3 ; not updated


// [x] update를 통해 해당 인덱스의 노드를 수정할 수 있어야 해요.
update(val) {
this.node.val = val;

return this;
}

// [x] remove를 통해 해당 인덱스의 노드를 삭제할 수 있어야 해요.
remove() {
if (!this.cnt) return this;

if (this.cnt === 1) {
this.node = null;

this.head = null;
this.tail = null;
} else {
this.nextNode.prev = this.node.prev;
this.prevNode.next = this.node.next;

if (this.head === this.node) this.head = this.node.next;
this.node = this.node.next;
}

this.cnt -= 1;

return this;
}

goPrev() {
if (this.cnt === 0) return this;
if (this.cnt === 1) return this.node;

this.node = this.node.prev;

return this;
}

goNext() {
if (this.cnt === 0) return this;
if (this.cnt === 1) return this.node;

this.node = this.node.next;

return this;
}

get length() {
return this.cnt;
}
}

const doubleLinkedList = new DoubleLinkedList();
7 changes: 7 additions & 0 deletions JavaScript/07주차/assignment/재영/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"main": "index.js",
"type": "module",
"scripts": {
"test": "node ./test.js"
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EOL 지켜주세요 😖

77 changes: 77 additions & 0 deletions JavaScript/07주차/assignment/재영/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { DoubleLinkedList } from "./index.js";

function expect(string = "", cb) {
if (!new.target) {
return new expect(string, cb);
}

this.isNot = false;

Object.defineProperty(this, 'not', {
get() {
this.isNot = !this.isNot;
return this;
},
});

this.toBeEqual = (val) => {
const res = cb();
const result = this.isNot ? res !== val : res === val;

const nowIsNot = this.isNot;

this.isNot = false;

console.log({ command: string, result, ...(!result ? { expectedValue: `${nowIsNot ? 'not ' : ''}${val}`, actualValue: res } : {}) });
return result;
}
}

function describe(title, tests) {
try {
console.log(`${title} 결과: `)
const res = tests.filter(v => v).length;
if (res === tests.length) {
console.log(`✅ CHECKED ${tests.length} TESTS SUCCEED!`)
} else {
throw new Error(`🔥 ${res}/${tests.length} TESTS SUCCEED. FAILED 😖`)
}
} catch(e) {
console.error("🤯 FAILED TEST!\n")
console.error(e)
}
}

describe("양방향 연결리스트", [
Copy link
Collaborator Author

@JengYoung JengYoung Dec 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헷갈리게 한 원흉(?)을 '양방향 원형 연결리스트'로 수정합니다.

expect("3개를 추가한 후 현재를 반환하면, 길이가 3개가 나와야 한다.", () => {
const doubleLinkedList = new DoubleLinkedList();
return doubleLinkedList.insert(1).insert(2).insert(3).length;
}).toBeEqual(3),
expect("마지막을 삭제하면, 마지막은 첫번째 머리 노드가 나와야 한다.", () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 양방향 연결리스트를 잘못 이해한 것인지 모르겠는데 이 부분이 잘 이해가 안돼서요..! 마지막을 삭제하면 마지막이 왜 첫번째 머리 노드가 되나요??

Copy link
Collaborator Author

@JengYoung JengYoung Dec 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아, 저는 양방향 원형 연결 리스트(Circularly Doubly Linked List)를 만들었어요!
언젠가 캐러셀에 쓰겠다고 마음을 먹어서, 마지막 것이 나오면 - 첫 번째 것이 나오도록 만들기는 했는데, 순수 양방향 연결 리스트에 꼭 필요한 기능은 아니었다고 생각이 되네요 😖

Copy link
Collaborator Author

@JengYoung JengYoung Dec 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제 코드가 헷갈리게 한 것 같은데, 미네님께서 하신 것이 원래 양방향 연결 리스트가 맞다고 보시면 돼요 :)
다만 저는 제 입맛에 맞게 좀 더 사짜(?)로 순환의 속성을 추가했다고 보는 게 타당하겠네요!
한글로 설명된 글을 보자면 다음 블로그 글을 참고할 수 있겠어요.
저는 여기서 더미 노드를 headtail 둘 다 명시했다고 보면 되겠네요!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하 그렇군요! 양방향 원형 연결 리스트도 한번 공부해보고 다시 코드를 보겠습니당 😂

const doubleLinkedList = new DoubleLinkedList();
return doubleLinkedList.insert(1).insert(2).insert(3).remove().nowNode.val;
}).toBeEqual(1),
expect("첫번째 머리 노드를 삭제하면, 추출 시 다음 노드의 앞은 꼬리 노드가 나와야 한다.", () => {
const doubleLinkedList = new DoubleLinkedList();
return doubleLinkedList.insert(1).insert(2).insert(3).goPrev().goPrev().remove().nowNode.val;
}).toBeEqual(2),
expect("첫번째 머리 노드를 삭제하면, 다음 머리는 다음 노드가 되어야 한다.", () => {
const doubleLinkedList = new DoubleLinkedList();
return doubleLinkedList.insert(1).insert(2).insert(3).goPrev().goPrev().remove().head.val;
}).toBeEqual(2),
expect("getNthNodeFromHead 메서드로 순회를 한다면 머리 노드를 기준으로 나와야 한다", () => {
const doubleLinkedList = new DoubleLinkedList();
return doubleLinkedList
.insert(1)
.insert(2)
.insert(3)
.insert(4)
.insert(5)
.insert(6)
.insert(7)
.insert(8)
.insert(9)
.getNthNode(8)
.val
}).toBeEqual(8)
]);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EOL 지켜주세요... 😭