함수형 프로그래밍 - 기본적인 패턴
함수형 프로그래밍에서 자주 사용되는 기본적인 패턴들을 알아보자.
우선 초기값인 users 객체를 생성하겠다.
Filtering
_filter 함수는 첫 번째 인자(list)로 값 또는 값을 리턴하는 함수를 받고 있고
두 번째 인자는 boolen값을 리턴하는 값이나 함수를 받고 있다.
new_list를 만들어 초기화 시키고 _each 함수를 사용해 반복문을 돌린다. ( _each에 대한 사항은 아래에서 설명 )
if문을 사용해서 predi에 들어온 함수가 필터링을 하여 참이면 true를 반환하여 new_list에 값을 저장한다.
_each의 반복문이 끝나면 최종 결과인 new_list를 반환하고 함수가 종료된다.
출력 결과
Mapping
_map 함수는 첫번째 인자(list)로 값 또는 값을 리턴하는 함수를 받고 있고, 두 번째 인자는 첫 번째 인자를 매핑할 함수를 받고 있다.
new_list를 만들어 초기화 시키고 _each 함수를 사용해 반복문을 돌린다. ( _each에 대한 사항은 아래에서 설명 )
list를 mapper에 정의한 매핑한 결과값을 new_list에 담고 _each 함수가 끝나면 new_list를 반환한다.
출력 결과
Iterating
_each 함수는 첫번째 인자(list)로 값 또는 값을 리턴하는 함수를 받고 있고, 두 번째 인자는 반복해서 무엇을 수행할지 정의한 함수가 온다.
for문을 이용하여 첫번째 인자의 크기만큼 반복하고 두 번째 인자로 들어온 함수를 반복하여 수행한 뒤 list를 반환한다.
Curring
_curryr함수는 인자로 본체 함수를 받고 함수를 리턴한다. 클로저로 인해 함수가 끝나더라도 자유 변수에 fn이 저장되어 사라지지 않는다.
이후 인자가 한번에 두 개가 들어오면 본체 함수가 실행되고 인자로 a, b가 순차적으로 들어간다.
하지만 인자가 따로 하나씩 들어오면 본체 함수가 실행될 때 인자로 b, a로 역순으로 들어가게 설계되어 있다.
sub에 _curryr 함수를 담으면서 fn함수를 정의하여 본체 함수를 담는다.
이후 sub에 한 번에 두 개의 인자를 넣거나 따로 두개의 인자를 담아서 원하는 순서대로 값을 변형시켜 적용시킬 수 있다.
출력 결과
Safe Accessor 1
_get 함수는 _curryr 함수와 자주 사용되는데 obj와 key 값을 정방향과 반대방향으로 유연성 있게 사용하기 위해서이다.
또한 값이 null일 때 오류가 발생하는 것을 방지하기 위해서 null일 경우 undefined를 반환하고
null이 아닌 경우 값을 반환하도록 설계되어 있다.
출력 결과
Reducing
add함수는 아래의 _reduce에 활용하기 위하여 만든 a + b의 간단한 함수이다.
slice는 배열 작업을 수행할 수 있는 내장함수인 Arrays.prototype.slice를 사용하여 배열의 일부 요소를 추출하여
새로운 배열을 반환하는 역할을 수행한다.
_rest함수는 첫 번째 인자(list)로 값 또는 값을 리턴하는 함수를 받고 있고, 두 번째 인자는 숫자를 받고 있다.
Arrays.prototype.slice의 call 함수를 사용해서 list의 값을 두번째 인자로 받은 숫자만큼 데이터를 제외하거나
두번째 인자값이 없을 경우 한 개의 데이터만 제외하고 배열을 복사하여 리턴하는 함수이다.
마지막 _reduce 함수는 첫 번째 인자(list)로 값 또는 값을 리턴하는 함수를 받고, 두번째 인자는 반복할 함수를
받고 있으며, 마지막 인자로 첫번째 인자를 가지고 반복하는 함수의 결괏값을 누적하여 저장할 데이터의 초기값을 받고 있다.
함수 안의 내용을 보면 _reduce의 인자로 값이 두 개(list, iter)만 왔을 때 memo(초기값)에 list의 첫 번째 데이터를 저장하고
_rest(list)를 수행하여 슬라이스를 통해 첫번째 데이터를 제외한 새로운 배열을 list에 저장한다.
이후 _each반복문을 통해 _reduce의 두 번째 인자로 받은 함수를 수행하면서 memo에 저장하고 그 결과를 반환한다.
출력 결과
Pipe ( 반환값 o )
_pipe 함수는 인자값들은 fns에 저장하여 또 다른 함수를 리턴한다. 그 함수에 인자값을 넣으면 _reduce 함수가 실행된다.
f1에 _pipe함수와 인자 3개를 넣으면 f1에 인자를 넣기 전까지 f1은 인자 하나를 대기하는 함수가 된다.
f1에 인자값을 넣으면 _reduce가 실행되고 _reduce의 첫 번째 인자로 함수 리스트가 전달되고 두 번째 인자로 iter에 해당하는
함수가 들어가서 arg는 memo의 초기값으로 들어가고 fn은 val로 최초에 받은 인자값들의 함수가 들어가서
함수가 인자값을 가지로 계산을 수행한 결과를 arg(memo)에 저장을 해나가고 그 결과를 반납한다.
출력 결과
go ( 반환값 x )
_go함수는 _pipe와 다르게 초기부터 초기값인 arg과 함수들인 arguments를 받는다.
fns에 함수들을 저장하고 _pipe.apply를 통하여 실행시키는데 첫번째 인자로 null값을 주었는데
그 이유는 apply의 첫번째 인수는 함수 내부에서 this가 가리킬 객체를 받고 두번째 인수로 함수에 전달할 인수를 배열로 받기 때문이다.
파이프에서는 인자가 최종적으로 두 가지가 들어와야 실행하기 때문에 다른 인자로 초기값인 arg를 넘겨준다.
이후 위에서 설명한 것과 같이 _pipe의 함수가 실행된다.
Safe Accessor 2
_map과 _filter를 _curryr를 활영하여 인자를 한 번에 두 개를 전달하면 정방향으로 한 개씩 따로 전달하면 역방향으로 동작하도록 설정.
이후 이전에는 obj가 null인지 확인하여 null이면 undefined를 반환하게 하였던 것처럼 _get에 "length" 인자를 전달한다.
이미 _curryr이 적용된 _get("length")을 사용하여 첫 번째 인자인 key값을 넘겨주고 _each함수 안의 for문에 _length는
아직 함수 상태이기 때문에 list인자를 받아 최종적으로 list.length가 완성하여 길이를 리턴하던지 undefined를 반환하게 된다.
따라서 아래의 출력 두 개 모두 에러가 뜨지 않고 undefinded를 반환하게 되어 안전한 함수를 완성하게 되었다.
출력 결과
Safe Accessor 3
이번에는 자주 사용되는 Object.keys 내장 함수를 안전한 함수로 전환해 보겠다.
4번째 코드와 같이 Object.keys를 실행하면 오류를 발생시킨다.
출력 결과
해결 방안
_get과 유사하게 만들면 되는데 _is_object함수는 받은 인자의 타입이 object 인지 또는 !! 불리언 값으로 강제로 형변환하는 기법을
사용하여 obj가 객체인 경우 ture를 반환하고 그 외의 경우에는 false를 반환하는 함수이다.
_keys함수는 받은 인자의 타입이 object인지 _is_object함수를 사용하여 ture면 object의 키 값을 반환하고
아니면 빈 배열을 반환한다. 따라서 마지막 코드에 에러가 발생하지 않고 빈 배열을 반환하게 된다.
출력 결과
_keys를 _each함수에 적용하여 객체의 키 값이 정리되지 않았을때 Object.keys를 사용하여 값을 가져오는 함수를 만들어보자.
출력 결과