본문 바로가기

Study/ETC

Clojure로 LISP 톺아보기 (2) - Symbol 과 함수

서론

지난 포스팅에서는 Clojure의 기본 데이터 구조에 대해 알아보았다. 이번 시간에는 Clojure의 변수를 저장하는 방식인 Symbol 과 함수에 대해 정리해 보겠다.

심볼은 값을 가르킨다.

심볼 평가시 심볼이 가르키는 값을 반환한다. def 함수를 통해 심볼을 생성한다.

user=> (def developer "PAPICO") #'user/developer user=> developer "PAPICO" user=> user/developer "PAPICO"

def 는 심볼에 직접 값을 바인딩하지 않고 var 를 통해서 한다. 현재 생성한 developer 는 디폴트 이름공간인 user 에 생성되었고, 현재 이름공간이 name 이므로 앞에 user/ 키워드를 제거해도 사용 가능하다.

let은 영역안에서만 심볼에 값을 바인딩한다.

이름공간에 전역적인 값을 추가시키지 말고, 임시적인 var 를 만들어 평가하고 싶을 경우 사용할 수 있는 키워드는 let 이다.

let 으로 생성한 심볼은 let 안에서만 평가된다. 밖에서 사용시 에러를 발생시킨다.

user=> (let [developer "ocipap"] developer) "ocipap" user=> developer "PAPICO"

지금까지 정리한 내용이다.

  • 전역 var 를 만들기 위해서는 def 를 사용
  • 지역 바인딩을 위해서는 let 사용

함수 만들기

클로저에서 함수를 여러가지 방법으로 선언할수 있다.

  • defn 을 사용하는 경우
  • 무명함수를 사용하는 경우 + 단축형
  • def + 무명함수 = 유명함수를 사용하는 경우

defn 을 사용하는 경우

defn 연산자의 기본형은 다음과 같다.

(defn [[함수 이름]] [[함수 인수 벡터]] [[함수 본문]])

실제 사용 예를 살펴보자.

user=> (defn add [a, b] (+ a b)) #'user/add user=> (add 1 2) 3

무명함수를 사용하는 경우 + 단축형

위 코드와 같이 add 같이 이름이 있는 함수를 유명함수, 이름 없이 사용하는 함수는 무명함수라고 한다. 주로 무명함수는 즉시 평가하는 로직에서 자주 사용된다.

무명함수는 fn 연산자를 이용해 생성한다.

user=> (fn [] (str "hello" "wolrd")) #object[user$eval157$fn__158 0xcc239ba "user$eval157$fn__158@cc239ba"] ; 함수 반환 user=> ((fn [] (str "hello" "wolrd"))) "hellowolrd" ; 호출

무명함수는 # 을 이용해서 단축형으로 코드를 줄일 수 있다. 이렇게 되면 인자는 % 이용해서 받고 두개 이상의 인자는 %1, %2 를 통해서 받는다.

user=> (#(str "Hello, " %) "papico") "Hello, papico"

def + 무명함수 = 유명함수를 사용하는 경우

def 연산자를 이용해 심볼을 생성하는 것을 했었다. defn 함수는 무명함수에 def 연산을 통해 이름을 바인딩하는 방식과 동일하다. 따라서 def 를 이용하면 무명함수에 이름을 바인딩 할수 있다.

user=> (def so-sleepy #(str % " is sleepy")) #'user/so-sleepy user=> (so-sleepy "papico") "papico is sleepy"

namespace 는 var나 클래스를 묶는 동적 맵핑이다.

이 앞에서 값을 바인딩할때 var 를 통해서 값을 바인딩하는 작업을 했었다. 그때 이 var 값이 저장되는 공간이 바로 namespace (이름공간) 이다. 디폴트 이름공간은 user 로 이때 바인딩한 varuser 이름공간에 저장된다.

ns 를 사용하여 새로운 이름공간을 만들고 이름 공간으로 전환할 수 있다.

user=> (ns developer.papico) nil developer.papico=> (def favorite-food "papico") #'developer.papico/favorite-food developer.papico=> favorite-food "papico" developer.papico=> developer.papico/favorite-food "papico"

위 코드는 developer.papico 라는 이름공간을 만들어 전환한 후 def 통해서 var 를 추가시켰다.

require를 통해 라이브러리는 사용한다.

각각의 이름공간에 할당된 값을 가져다가 사용할 때는 require 를 이용한다.

이때 이름공간에 할당한 이름이 같은 경우를 대비해 대부분 :as 로 별칭을 지정해 가져온다.

이 앞에서 사용했던 set 컬렉션을 별칭으로 가져와 사용하는 예시 코드를 작성해 보겠다.

(ns settest (:require [clojure.set :as s])) (defn get-intersection [a b]     (let [a-set (set a)         b-set (set b)         common-set (s/intersection a-set b-set)]     (str "common value : " common-set))) (get-intersection [:a :b :c :d] [:b :c :q :w])  ; => "common value : #{:c :b}"

마무리

이번 포스팅에서는 varlet 을 통한 바인딩과, 함수, 이름공간에 대해 정리해보았다.

이번 포스팅을 요약한 내용이다.

  • 전역 var 를 만들기 위해서는 def 를 사용
  • 지역 바인딩을 위해서는 let 사용
  • 함수의 종류에는 무명함수와 유명함수가 있다.
  • defnfn 을 이용해 함수를 만들 수 있다.
  • namespacevar 를 묶는 동적 맵핑이다.
  • ns 를 통해 이름공간을 생성/이동할수 있고, require 를 이용해 다른 이름공간에 할당된 데이터를 가져올 수 있다.

다음 포스팅에서는 클로저에서 제어문을 사용하는 방법과 함수형 전환을 하기전 클로저에서 제공하는 기본적인 함수들을 익혀보도록 하겠다.