2016년 3월 30일 수요일

The first Clojure - 02.구조

Eclipse Clojure IDE 사용 방법
  1. 클로저 프로젝트 생성
New > Clojure Project > 프로젝트 이름넣고, Leiningen template은 default
  1. 클로저 네임스페이스 생성(이게 Java의 Class이고 Package입니다.)
src 위치에서 > New > Clojure Namespace > Name 입력(PigLatin) > Finish
  1. 클로저 코드 실행해보기
생성한 네임스페이스에 코드를 작성하고 > 편집 창에서 오른쪽 마우스 > Clojure > load file in REPL


이렇게 사용하면 되는데, 실행 시 시스템 메모리가 부족하다는  오류가 많이 발생한다.


(ns PigLatin)
; This is Java code.
;public class PigLatin {
;
; public static String pigLatin(String word) {
;     char firstLetter = word.charAt(0);
;     if ("aeiou".indexOf(firstLetter) != -1) return word + "ay";
;     return word.substring(1) + firstLetter + "ay";
; }
;
; public static void main(String args[]) {
;        System.out.println(pigLatin("red"));
;        System.out.println(pigLatin("orange"));
; }
;}
; This is Clojure code.
; When a set is used as a function, it returns the argument if it is
; in the set and nil otherwise.  When used in a boolean context,
; that indicates whether the argument is in the set.
(def vowel? (set "aeiou"))
(defn pig-latin [word] ; defines a function
 ; word is expected to be a string
 ; which can be treated like a sequence of characters.
 (let [first-letter (first word)] ; assigns a local binding
(if (vowel? first-letter)
  (str word "ay") ; then part of if
  (str (subs word 1) first-letter "ay")))) ; else part of if
(println (pig-latin "red"))
(println (pig-latin "orange"))


위 예제는 동일 문제를 자바와 클로저 코드로 보여주고 있다. pigLagin 함수는 주어진 문자열의 첫 번째 문자가 아, 에, 이, 오, 우 발음이면 문자열에 “ay”만 추가하고, 아니면 나머지 문자열에 다음에 첫번째 문자열을 더하고 “ay”을 추가한다.


Clojure 언어만 특정을 좀 더 찾아봤다. clojure.or.kr 에서 발췌 한 내용이다.


불변 데이터
클로저에서는 모든 데이터는 불변 데이터이다. 데이터는 한 번 생성되면 변경 불가능. 불변 데이터는 순수 함수와 불가분의 관계. 순수 함수의 파라미터가 불변 데이터가 아니라면, 그래서 함수 실행중에 다른 곳에서 파라미터의 값이 변한다면 순수 함수가 될 수 없다.


불변 데이터의 이점
  • 쓰레드 안전: 불변 데이터는 변경이 않되고 읽기만 가능하기  때문에 언제 어디서나 참조해도 전혀 문제가 되지 않는다.
  • 쉬운 테스트: 테스트의 목적은 변화에 대한 대응이다. 변화가 적으면 테스트도 적어진다. 불변 데이터의 사용은 변화되는 데이터의 영역을 최소한으로 줄여 테스트 범위가 줄어든다.


존석성 데이터 구조(Persistent Data Structure)
프로그램에서 데이터를 변화시킬 필요가 있을  때는 어떻게 할까? 클로저는 존속성 데이터구조로 아주 효과적으로 지원한다. 존속성 데이타 구조란 데이터의 갱신이 기존 데이타의수정을 통해서가 아니라 기존 데이타를 그대로 유지한 채 신규 데이타와의 차이만을 주가함으로서 이루워지는 데이타 구조를 말한다.

(def a (list 1 2 3))
(def b (conj a 0))
a 라는 리스트가 만들어 진 후,  a에 0을 추가하여 b를 만들면, 리스트 b는 리스트 a와 자료 구조를 공유하고 있게 된다. 리스트 b는 리스트 a의 값을 전부 복사하는 것(자바라면 이렇게 하겠지만)이 아니라 리스트 a의 기존 값들에 0을 추가하여 새로운 리스트를 만들어 낸 것이 리스트 b이다.


객체지향적 특징
클로저는 객체지향 언어가 아니다. 그렇지만 객체지향 언어가 가지는 장점은 다형성을 지원한다.


  • 다형성: 클로저는 다형성을 멀티메소드와 프로토콜을 이용해 지원하고 있는데, 보다 개방적이고 확장성이 높다.
  • 캡슐화: 클로저의 함수는 무없을 하는지에 대한 관점이 아니라 무엇을 입력받고 리턴하는지에 대한 관점에서 볼 수 있으며, 함수안에는 블럭, 로컬 캡슐화가 가능하다.
  • 모듈화:자바의 클래스와 패키지가 하는 모듈화를 클로저는 네임스페이스가 지원한다.

위 내용을 간략하게 정리한다면 클로저가 Data관점의 언어라는 것을 다시 설명한 것일 것이다.
객체지향 언어가 세상을 객체(Object)로 보고 그 행위를 시뮬레이션 한다면, 클로저는 세상을 Data로 보며 그 데이터는 불변하다고 접근한다는 것이다. 이 때문에 멀티 쓰레드 환경에서의 프로그래밍 상의 이점도 가지게 되고.


Vars
Var는 클로저에서 변화를 처리하는 한 가지 방법이다. Var는 다른 언어의 변수와 같은 것은 아니지만 비슷한 역할을 한다. 클로저에서 Var는 스레드별로 동적으로 다시 바운딩되는 변화가능한 저장 장소로서의 역할을 한다. 모든 Var는 대개 루트 바운딩되는데, 이 루트 바운딩은 모든 스레드에서 공유된다. 심볼에 의해 이름 지어진 하나의 Var는 하나의 값을 가리킨다. 이 값은 변할 수 있다.


Local Binding let
let은 let문구가 사용되는 영역에서 참조하는 심볼을 만든다. 로컬 변수를 만드는 것과 같다.


Thread Binding
By default Vars are static, but Vars can be marked as dynamic to allow per-thread bindings via the macro binding. Within each thread they obey a stack discipline:


user=> (def ^:dynamic x 1)
user=> (def ^:dynamic y 1)
user=> (+ x y)
2

user=> (binding [x 2 y 3]
        (+ x y))
5

user=> (+ x y)
2


Vars Binding TestCode
(ns Vars)
(def ^:dynamic v 1) ; v is a global binding
(defn f1 []
 (println "f1: v:" v))
(defn f2 []
 (println "f2: before let v:" v)
 ; creates local binding v that shadows global one
 (let [v 2]
; local binding only within this let statement
(println "f2: in let, v:" v)
(f1))
 ; outside of this let, v refers to global binding
 (println "f2: after let v:" v))
(defn f3 []
 (println "f3: before binding v:" v)
 ; same global binding with new, temporary value
 (binding [v 3]
; global binding, new value
(println "f3: within binding function v: " v)
(f1)) ; calling f1 with new value to v
 ; outside of binding v refers to first global value
 (println "f3: after binding v:" v))
(defn f4 []
(def v 4)) ; changes the value of v in the global scope
(println "(= v 1) => " (= v 1))
(println "Calling f2: ")
(f2)
(println)
(println "Calling f3: ")
(f3)
(println)
(println "Calling f4: ")
(f4)
(println "after calling f4, v =" v)


실행결과
(= v 1) =>  true
Calling f2:
f2: before let v: 1
f2: in let, v: 2
f1: v: 1
f2: after let v: 1

Calling f3:
f3: before binding v: 1
f3: within binding function v:  3
f1: v: 3
f3: after binding v: 1

Calling f4:
after calling f4, v = 4
nil

ETL 솔루션 환경

ETL 솔루션 환경 하둡은 대용량 데이터를 값싸고 빠르게 분석할 수 있는 길을 만들어줬다. 통계분석 엔진인 “R”역시 하둡 못지 않게 관심을 받고 있다. 빅데이터 역시 데이터라는 점을 볼때 분산처리와 분석 그 이전에 데이터 품질 등 데이...