성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>함수형 프로그래밍 개념 - 리스트 해석(List Comprehension)과 순수 함수</h1> <p> 절차형 프로그래밍만 하다가 함수형 프로그래밍을 배우는 경우 용어에서 약간 멈칫하는 경우가 있습니다. 그냥 오늘은 혹시 도움이 되실까 싶어 2가지 정도 소개해 드릴까 합니다.<br /> <br /> 우선, 리스트 해석(List Comprehension)인데, 다음의 글을 보시면 이해가 화~~~악 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Chapter 3. Comprehensions ; <a target='tab' href='http://juehan.github.io/DiveIntoPython3_Korean_Translation/comprehensions.html'>http://juehan.github.io/DiveIntoPython3_Korean_Translation/comprehensions.html</a> </pre> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> 리스트 컴프리헨션(list comprehension)이란 특정 리스트의 각각의 원소에 어떤 함수를 적용한 후 그 결과를 받아 새로운 리스트로 만들 수 있는 아주 간편한 방법<br /> </div><br /> <br /> 위의 글에 소개된 파이썬 예제 코드를 보면 더욱 이해가 빠르겠지요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > a_list = [1, 9, 8, 4] [elem * 2 for elem in a_list] // a_list의 모든 요소에 * 2를 해서 새로운 리스트를 만듦 </pre> <br /> 사실 C#에도 LINQ가 나오면서 어느 정도 이런 구문이 가능합니다. 그렇습니다. SELECT 구문이 바로 그것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > List<int> list = new List<int>() { 1, 2, 3, 4 }; <span style='color: blue; font-weight: bold'>var list2 = from elem in list select elem * 2;</span> foreach (var item in list2) { Console.WriteLine(item); } </pre> <br /> Entity Framework이 하도 유명해서 LINQ를 데이터베이스 접근 용도로만 아실 수 있는데, 엄밀히 LINQ는 C#에 어느 정도의 함수형 프로그래밍 요소를 가져온 기여도 무시할 수 없습니다.<br /> <br /> 이 외에, "<a target='tab' href='http://juehan.github.io/DiveIntoPython3_Korean_Translation/comprehensions.html'>Chapter 3. Comprehensions</a>" 글을 보면 "comprehension"이란 단어가 컬렉션의 요소에 어떤 함수를 적용한 후 새로운 컬렉션을 반환하는 일반적인 용도로 쓰인다는 것을 알 수 있습니다. 즉, "Dictionary Comprehension" 등의 표현도 있다는 것이지요.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그다음, 순수 함수(Pure function)가 있습니다. "<a target='tab' href='http://www.yes24.com/24/Goods/3907543?Acode=101'>프로그래밍 클로저: Lisp</a>" 책에 보면 순수 함수는 말 그대로 부수 효과(side effect)가 전혀 없는 함수를 말하며, 즉 오로지 인자에만 의존해서 결과가 만들어지고 반환 값으로만 외부 세계에 영향을 준다고 합니다. 이렇게 말하면 ^^ 잘 이해가 안되죠? 역시 코드를 곁들여 설명해 보겠습니다.<br /> <br /> 간단하게 예를 들어, 다음의 경우는 순수 함수가 아닙니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int GeneralMethod(int n) { return n * DateTime.Now.Ticks; } </pre> <br /> GeneralMethod에 n == 1을 넣었을 때 항상 같은 값이 산출되는 것이 아니기 때문입니다. 순수 함수는, 언제나 동일한 입력 값이면 동일한 출력 값을 반환해야 합니다. <br /> <br /> 또한, 객체 내부 상태를 변경해서도 안됩니다. 이것은 어찌 보면 당연한 조건입니다. 객체의 내부 값을 변경하는 경우 해당 함수를 호출할 때마다 상태가 변경되므로 입력 값에 대한 출력 값이 '항상' 일치하지 않을 수 있기 때문입니다. (C++의 경우에는 const 함수가 적당한 예가 되겠군요.)<br /> <br /> 그런 의미에서 우리가 알고 있는 수학 함수라는 것들은 모두 '순수 함수'에 속합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int a = Math.Abs(-5); </pre> <br /> 위의 C# 메서드는 5를 반환합니다. -5 입력 값이 들어오면, 무조건, 항상, 언제나 한결 같이 5를 반환합니다. 이것이 순수 함수입니다.<br /> <br /> 자연스럽게 순수 함수가 만연하게 되는 함수형 프로그래밍은 이런 특성으로 인해 절차형에 비해 호출에 대한 최적화가 가능합니다. 왜냐하면, Math.Abs 함수의 입력으로 -5가 들어왔다면 최초 한번은 해당 함수의 전체 코드를 실행해야 하지만, 이후부터는 입력값 -5에 대해 무조건 5를 반환하면 되기 때문입니다.<br /> <br /> 실제로 Clojure 언어에는 메모이제이션(memoization)이라는 구문이 제공됩니다. 예를 들어, 1초의 지연 시간을 일부러 포함한 다음의 clojure 함수를 정의한 후,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > user=> <span style='color: blue; font-weight: bold'>(defn delay-print [] (Thread/sleep 1000) (println "done") 100)</span> #'user/delay-print </pre> <br /> 이 함수를 memoize 함수를 통해 메모이제이션을 수행하도록 할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > user=> <span style='color: blue; font-weight: bold'>(def delay-print (memoize delay-print))</span> #'user/delay-print </pre> <br /> 그럼, delay-print 함수를 최초 실행했을 때는 1초의 지연 시간을 갖고 println 함수도 수행한 후 100의 정수값을 반환하지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > user=> <span style='color: blue; font-weight: bold'>(delay-print)</span> done 100 </pre> <br /> 이후에 다시 실행했을 때는 1초의 지연시간도, println 함수 수행도 없이 그냥 곧바로 100의 값을 반환합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > user=> <span style='color: blue; font-weight: bold'>(delay-print)</span> 100 </pre> <br /> 이걸 테스트 하다보면 nCr 조합의 값을 재귀호출을 통해 얻어내던 것이 생각납니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 동전을 여러 더미로 나누는 경우의 수 세기(Partition Number) - 두 번째 이야기 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1719'>http://www.sysnet.pe.kr/2/0/1719</a> </pre> <br /> 위의 경우 C#으로 만든 이항 계수 재귀 함수가 가장 낮은 성능을 보여줬는데요. clojure의 경우 다음과 같이 memoize를 한번 해주면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > user=> <span style='color: blue; font-weight: bold'>(defn BC [n, r] (cond (or (= r 0) (= r n)) 1 :else (+ (BC (- n 1) (- r 1)) (BC (- n 1) r)) ) )</span> #'user/BC user=> <span style='color: blue; font-weight: bold'>(def BC (memoize BC))</span> #'user/BC </pre> <br /> 결과가 순식간에 출력됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > user=> (time <span style='color: blue; font-weight: bold'>(BC 30 12)</span>) "Elapsed time: 0.67197 msecs" 86493225 // 위의 time 함수는 수행 시간을 재주는 기능을 합니다. // 만약, memoize를 하지 않으면 (제 컴퓨터 기준으로) 수행 시간이 15초 정도 소요됩니다. </pre> <br /> 아니, 그런데 위의 경우에는 (BC 30 12) 호출을 두 번째 한 것도 아닌데 어떻게 저리 빠른 실행 결과를 보여줄 수 있을까요? 왜냐하면, BC 함수가 재귀 호출이 되므로 내부의 호출 결과들이 모두 메모이제이션되기 때문에 내부 BC 함수 수행 중에 캐시된 결과값이 도움이 된 경우가 있었기 때문입니다.<br /> <br /> 만약 C#으로 순수 함수의 성능을 높이려면 일부러 결과를 캐시(cache)하는 작업을 해줘야 하는데 clojure의 경우 단순하게 memoize 함수의 사용으로 그것이 해결됩니다. (실제로 "<a target='tab' href='http://www.sysnet.pe.kr/2/0/1719'>동전을 여러 더미로 나누는 경우의 수 세기(Partition Number) - 두 번째 이야기</a>" 글의 "Partition Number" 예제에서는 EulerFunction 타입의 P 메서드에 캐시를 추가해서 수행 속도를 높였습니다.)<br /> <br /> 그러고 보니 '순식간'에라는 표현으로 과거에 썼던 글이 하나 생각나는 군요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 함수형 언어의 코드가 그렇게 빠를까? ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1324'>http://www.sysnet.pe.kr/2/0/1324</a> </pre> <br /> 솔직히, 위의 글을 쓰던 순간에는 함수형 언어에 대한 이해가 많이 부족했었는데요. 이제는 왜 함수형 언어들이 빠를 수가 있을까... 하는 것이 이해가 됩니다. 또한 위의 글에서 테스트 했던 haskell과 F#의 메모리 사용량도 이해가 됩니다. 어쩌면, 나름대로의 함수형 언어라는 특징을 기반으로 내부적인 수행 최적화를 더 할 수도 있었을 테고 그 일정 부분에는 메모이제이션과 같은 캐시 역할도 수반되었을지도 모르겠습니다.<br /> <br /> 암튼, 요즘 몇 가지 언어를 쭈욱 훑어보다 보니 이해의 폭이 넓어져서 좋은 것 같습니다. ^^<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
8834
(왼쪽의 숫자를 입력해야 합니다.)