변경 가능한 데이터 구조를 가진 언어에서 불변성 유지하기
May 13, 2024
쏙쏙들어오는 함수형 코딩
# 동작을 읽기와 쓰기로 분류하기
읽기(read)
- 데이터에서 정보를 가져옴.
- 데이터를 바꾸지 않음.
쓰기(write)
- 데이터를 바꿈
이 중에서 쓰기는 불변성 원칙에 따라 구현해야함.
불변성 원칙은 카피-온-라이트(copy-on-write)라고 함.
# 카피-온-라이트(copy-on-write)
카피-온-라이트에는 원칙이 있다.
- 복사본 만들기
- 복사본 변경하기(원하는 만큼)
- 복사본 리턴하기
이 원칙을 지키면서 카피-온-라이트를 사용한다면 불변성을 지킬 수 있음.
카피-온-라이트 사용예시
function add_element_last(array, item) {var new_array = array.slice();new_array.push(item);return new_array;}
add_element_last는 쓰기(write)일까 읽기(read)일까? 값을 복사했고, 원본 데이터를 변경하지 않고 복사한값을 리턴했기때문에 읽기(read)라고 할 수 있음.
카피-온-라이트는 읽기를 쓰기로 바꿈.
불변성을 고려하지 않은 코드를 수정해보자
이전 코드
function remove_item_by_name(cart, name) {var idx = null;for(var i=0; i<cart.length; i++) {if(cart[i].name === name) {idx = i;}}if(idx !== null) {cart.splice(idx, 1)}}
수정 한 코드
function remove_item_by_name(cart, name) {var new_cart = cart.slice(); // 복사본 만들고var idx = null;for(var i=0; i<new_cart.length; i++) {if(new_cart[i].name === name) {idx = i;};};if(idx !== null) {new_cart.splice(idx, 1) // 복사본 수정 후}return new_cart; // 복사본 리턴}
카피-온-라이트 연습 문제
밑에 있는 코드를 카피-온-라이트 형식으로 바꿔보세요.
var mailing_list = [];function add_contact(email) {mailing_list.push(email);}function submit_form_handler(event) {var form = event.target;var email = form.elements["email"].value;add_contact(email);}
나의 풀이
var mailing_list = [];function add_contact(list, email) {var new_mailing_list = list.slice();new_mailing_list.push(email);return new_mailing_list;}function submit_form_handler(event) {var form = event.EventTarget;var email = form.elements["email"].value;mailing_list = add_contact(mailing_list, email);}
의도와 맞게 잘 풀었음. 전역변수를 받아서 복사본을 만들고 수정후 복사본을 리턴한 결과값을 사용부에서 전역변수에 할당했음.
# 쓰면서 읽기도 하는 함수를 분리하기
- 읽기 함수와 쓰기 함수로 분리하기
- 값 두개를 리턴하는 함수로 만들기
위와 같이 두 가지 방법이 있음.
읽기 함수와 쓰기 함수로 분리하기 예시
// 읽기 함수function first_element(array) {return array[0];}// 쓰기 함수function shift() {array.shift();}
값 두개를 리턴하는 함수로 만들기 예시
function shift(array) {var array_copy = array.slice();var first = array_copy.shift();return {first,array: array_copy}}
연습 문제1.
push 메서드를 카피-온-라이트로 만들어보세요.
나의 풀이
function copy_on_write_push(array, item) {const copy_array = array.slice();copy_array.push(item);return copy_array;}
정답임.
연습 문제2.
카피-온-라이트 버전의 push함수를 사용해 add_contact 함수를 리팩토링 해보세요.
이전 코드
function add_contact(mailing_list, email) {var list_copy = mailing_list.slice();list_copy.push(email);return list_copy;}
나의 코드
function add_contact(mailing_list, email) {return copy_on_write_push(mailing_list, email)}
정답임.
연습 문제3.
배열 항목을 카피-온-라이트 방식으로 설정하는 arraySet 함수를 만들아보세요.
예시
a[15] = 2;
나의 풀이
function arraySet(array, idx, value) {var list_copy = array.slice();list_copy[idx] = value;return list_copy;}
정답임.
# 객체에 대한 카피-온-라이트
배열에서 카피-온-라이트를 하는 방법은 slice가 있다면, 객체에는 Object.assign이 있음.
객체 카피-온-라이트 예시
var object = {a: '1', b: '2'};var copy_object = Object.assign({}, object);
Object.assign으로 객체를 복사 할 수 있지만, 중첩(nested)된 객체 구조는 다 복사하지 못함. 이유는 Object.assign은 얕은복사이기 때문임.
#참고 얕은 복사는 중첩데이터에서 최상위 데이터만 복사함.