Published
Edited
May 13, 2020
1 fork
1 star
Insert cell
Insert cell
// 함수형 프로그래밍이 뭔가요? 영상의 뒷부분에 나오는 예제를 따라해 보자
// 이과생으로 선착순 3명 단기알바 선발해 "이름(전공)" 형태로 스페이스로 구분된 출력으로 공지하는 프로그램
students = [
new Student("홍길동","문과","문학"),
new Student("전우치","이과","기계"),
new Student("임꺽정","이과","화학"),
new Student("일지매","문과","언론"),
new Student("장길산","예체능","체조"),
new Student("연흥부","이과","컴퓨터"),
new Student("연놀부","이과","수학"),
new Student("옹고집","문과","경영"),
new Student("이몽룡","문과","정치"),
new Student("연오랑","예체능","사진"),
]
Insert cell
function Student(name, division, major) { // 생성자 함수
this.name = name
this.division = division
this.major = major
}
Insert cell
// 함수형이 아닌 명령형 스타일 날코딩
{ // 기계상태의 변화를 단계별로 일일이 모니터링하며 제어하는 방식
let filtered = []

// 이과 학생들을 filtered 배열에 담기
for (let i=0; i<students.length; ++i) {
if (students[i].division === "이과")
filtered.push(students[i])
}
// splice로 첫 3행만 잘라내기
filtered = filtered.splice(0, 3)
let result = ""
for (let i=0; i<filtered.length; ++i) {
// 이름과 전공명 포맷해 출력 스트링에 추가
const s = filtered[i]
result += `${s.name}(${s.major}) `
}
return result
}
Insert cell
// 함수형 스타일 고차함수 등의 메소드 활용
// 식에 함수를 적용하면 어떤 결과값이 계산되는지를 중심으로 생각

// students.filter( ... ).slice().map( ... ).join(" ")
students
.filter( s => s.division==="이과" )
.slice(0,3)
.map( s => `${s.name}(${s.major})` )
.join(" ")
Insert cell
Insert cell
// uncurried function 커리되지 않은 함수
addU = (x,y) => x+y
Insert cell
addU(3,4)
Insert cell
subU = (x,y) => x-y // 첫번째 수에서 두번째 수를 빼는 함수
Insert cell
subU(3,7)
Insert cell
// 첫번째와 두번째 인자를 바꿔주는 고차함수
flipU = f => (a,b) => f(b,a)
Insert cell
// 두번째 수에서 첫번째 수를 빼는 새로운 함수를 직접 작성하지 않고 subU를 변신시키자! flipU를 활용해서
g1 = flipU(subU)
Insert cell
g1(3,7)
Insert cell
// curried function 커리된 함수 (기본적으로 함수를 돌려주는 고차함수)
addC = x => y => x+y
Insert cell
// 부분 적용으로 새로운 함수를 만들어낼 수 있다
addC(2) // y => 2+y
Insert cell
addC(2)(3)
Insert cell
subC = x => y => x-y
Insert cell
subC(3)(7)
Insert cell
flipC = f => a => b => f(b)(a)
// 이 정의 자체는 에러가 아니고 아래 에러가 나는 셀이 있는데 그와 관련된 실행 부분을 빨간색으로 표시해주는 것임
// TypeError가 나는 셀을 찾아 주석처리하면 위의 빨간색 표시도 없어짐
Insert cell
// 새로운 함수를 직접 작성하지 않고 subC를 변신시켜서
// 나중에 호출하는 두번째 인자에서 먼저 호출하는 첫번째 인자를 빼는 함수를 만들자
g2 = flipC(subC)
Insert cell
g2(3)(7)
Insert cell
// 우리는 uncurried로 뺄셈 함수 subU를 작성했는데 (우리는 uncurried로 작업을 주로 하고 싶다)
// 외부 라이브러리가 curried로 작성되어 있어서 flipC를 제공하고 있다.
// 이런 상황에서 subU를 flipC를 이용해 변신시키고 싶은데 뭔가 아귀가 맞지 않는 문제!
g3 = flipC(subU)
Insert cell
g3(3,7) // 이렇게 해도 답이 아니고
Insert cell
g3(3)(7) // 이렇게 해도 에러
Insert cell
// 해결책 uncurried 함수를 curried 함수로 변신시키는 고차함수
curry = f => a => b => f(a,b)
Insert cell
// 우리가 subU만 갖고 있고 subC를 작성해 놓지 않았더라도 subC와 같은 함수를 만들어 낼 수 있다
subC2 = curry(subU)
Insert cell
subC2(3)(7)
Insert cell
g4 = flipC( curry(subU) )
Insert cell
// flipC(subC)를 한 것과 같은 함수, 즉 두번째 인자에서 첫번째 인자를 빼는 커리된 함수
g4(3)(7)
Insert cell
// 여기까진 좋은데 ... 우리는 g4처럼 curried 함수가 아닌 uncurried를 최종적으로 얻어내고 싶다
// 반대로 curried 함수를 uncurried로 변신시키는 고차함수
uncurry = f => (a,b) => f(a)(b)
Insert cell
g5 = uncurry(g4)
Insert cell
g5(3,7)
Insert cell
// 우리는 uncurried로 뺄셈 함수 subU를 작성했는데 (우리는 uncurried로 작업을 주로 하고 싶다)
// 외부 라이브러리가 curried로 작성되어 있어서 flipC를 제공하고 있다.
// 이런 상황에서 subU를 flipC를 이용해 변신시키고 싶다면 curry와 uncurry 고차함수를 곁들여 주면 가능하다!
Insert cell
g6 = uncurry(flipC(curry(subU))) // flipU(subU) 와 같은 효과
Insert cell
g6(3,7)
Insert cell
// 그럼 아예 filpU를 flipC로부터 변신시켜서 만들어 내면 어떨까?
// 바로 합성함수 고차함수가 있으면 된다
compU = (f,g) => x => f(g(x))
Insert cell
myFlipU = compU(uncurry, compU(flipC,curry)) // 수식으로는 uncurry o flipC o curry
Insert cell
myFlipU(subU)(3,7)
Insert cell
Insert cell
I = a => a
Insert cell
K = a => b => a // 상수함수
Insert cell
M = f => f(f) // 간단한 함수지만 끝나지 않는 계산을 만들어낼 수 있다
Insert cell
Insert cell
C = f => a => b => f(b)(a) // 지난 영상에서 했던 flipC이다
Insert cell
I("x")
Insert cell
I(3)
Insert cell
I(I) === I
Insert cell
( (a=>a)(b=>c=>b) )("x")(e=>"f")
Insert cell
M(I) === I // (f=>f(f))(I) === I(I) === (a=>a)(I) === I
Insert cell
// M(M) // stack overflow
Insert cell
K(5)(10)
Insert cell
[5,10].reduce( (g,x) => g(x), K ) // K(5)(10) 에 해당한다는 것을 이해해 보라
Insert cell
K(M)(I) === M
Insert cell
K(I)(M) === I
Insert cell
K(I)("x") === I
Insert cell
K(I)("x")("y") === "y"
Insert cell
KI = a => b => b // K(I) 와 같은 일을 하는 함수
Insert cell
KI(M)(K) === K
Insert cell
KI(K)(M) === M
Insert cell
C(K)(I)(M) === M
Insert cell
KI(I)(M) === M
Insert cell
I.toString() // 되긴 하는데
Insert cell
K.toString() // 되긴 하는데
Insert cell
K(I).toString() // 이건 좀 아님 ... K의 인자 a에 실제로 넘긴 값인 I로 (즉, a => a 로) 치환하지 않음 :(
// 실제로는 b => a => a 이런 식으로 표시가 되어야 실제 계산과정의 내용을 알아볼 수 있겠죠
Insert cell
Insert cell
arity = { // 몇 개의 인자를 연속해서 받을 수 있나
const ar = c => {
if (typeof c === "function") {
try { return 1 + ar( c({}) ) } catch(_) { return 1 }
} else { return 0 }
}
// ObservableHQ 환경의 전역변수로는 직접 재귀함수를 정의가 안되니 지역변수 ar함수를 정의한 다음에 리턴
return ar
}
Insert cell
[I, K, M, C].map(arity)
Insert cell
[M(I), K(I), C(K)].map(arity)
Insert cell
show = c => {
const n = arity(c)
// arity 26까지만 일단 생각하자 ... 넘어가는 무지복잡한 녀석은 처리하기 귀찮다
if (n > 26) throw RangeError('arity('+c+')='+n+' is over 26')
const as = ["a","b","c","d","x","y","z","w",
"u","v","p","q","r","s","t",
"e","f","g","h","i","j","k",
"l","m","n","o"].slice(0,n)
const mkF = a => {
const f = x => mkF( a+"("+x+")" )
f.toString = () => a
return f
}
const fs = as.map( mkF )
const head = as.join(" => ")
const body = fs.reduce( (g,x) => g(x), c ).toString()
return ( (n>0)? head+" => ": "") + body // arity 0인 경우는 다루지 않겠지만 혹시 모르니 ...
}
Insert cell
CC = f => a => b => f(b)
Insert cell
[I, K, M, C].map(show)
Insert cell
[M(I), K(I), C(K)].map(show)
Insert cell
Insert cell
TRUE = a => b => a // 인자를 두 번에 걸쳐 받아서 첫번째 것을 돌려주는 함수
Insert cell
FALSE = a => b => b // 인자를 두 번에 걸쳐 받아서 두번째 것을 돌려주는 함수
Insert cell
// 진리값을 사용하는 이유는 if c then x1 else x2 같은 계산을 하기 위해서
IF_THEN_ELSE = c => x1 => x2 => c(x1)(x2)
// IF_THEN_ELSE = c => c // 더 간단하게는 이렇게 정의할 수도 있다
Insert cell
// 실제로도 실행을 해보자
IF_THEN_ELSE(TRUE)("e1")("e2")
Insert cell
// 실제로도 실행을 해보자
IF_THEN_ELSE(FALSE)("e1")("e2")
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more