-
Couldn't load subscription status.
- Fork 8
Living Clojure 02
Kyunggook Kim edited this page Aug 15, 2016
·
1 revision
-
리스트는 LISP 언어의 본질에서 유래한다.
- LISP == LISt Processing
-
리스트에서 인용기호가 필요한 이유
- LISP에서는 식의 첫 요소를 연산자나 함수로 인식하기 때문
;; 인용기호가 괄호 앞에 있음으로 리스트 자료구조로써 인식됨
'("marmalade-jar" "empty-jar" "pickle-jam-jar")
;=> ("marmalade-jar" "empty-jar" "pickle-jam-jar")
;; 인용기호 없이 괄호로 시작되면 연산자나 함수로 인식됨 == 문자열은 함수가 아님
("marmalade-jar" "empty-jar" "pickle-jam-jar")
;=> ClassCastException java.lang.String cannot be cast to clojure.lang.IFn
;; 인용기호를 괄호 앞에 붙이면 결과는 숫자 2가 아닌 식을 반환
'(+ 1 1)
;=> (+ 1 1)
;; 첫째 요소는 연산자이다.
;; 둘째 요소는 정수 1이다.
;; 셋째 요소는 정수 1이다.
(first '(+ 1 1))
;=> +- 코드가 데이터로 취급될 수 있다!
- 모든 클로저 코드는 데이터의 리스트로 구성되어 있다.
- 클로저 심볼은 값을 가리킨다.
- 심볼이 평가되면 그 심볼이 가리키는 값을 반환한다.
-
def는 값에 이름을 줘서 나중에 다른 곳에서 그 값을 참조할 수 있게 한다. -
def는 심볼에 값을 직접 바인딩 하지 않고var를 통해서 한다. -
var는 다른 언어들에서의 변수와 다르다. 프로그램이 수행되면서 그 값이 변하지 않기 때문이다. - 모든 값에 대해 전역적인
var객체를 만들고 싶지 않고, 임시의var객체 생성이 필요한 경우let을 사용한다. -
let을 사용하면let영역 안에서만 사용되는 심볼에 값을 바인딩할 수 있다. -
let바인딩은 벡터 안에 심볼과 값의 쌍들로 구성된다.
;; def는 REPL의 디폴트 이름공간인 user에 developer를 위한 var 객체 생성
(def developer "Alice") ;; developer의 바인딩을 "Alice"로 변경
;=> #'user/developer ;; 심볼 var 객체 생성
developer ;; 심볼을 통해 값 호출
;=> "Alice"
user/developer ;; 이름공간을 포함한 심볼을 통해 값 호출
;=> "Alice"
;; developer의 바인딩을 "Alice"로 변경
(def developer "Alice")
;=> #'user/developer ;; 심볼 var 객체 생성
;; let 안에서 developer의 바인딩을 임시로 "Alice in Wonderland"로 변경
(let [developer "Alice in Wonderland"] ;; 심볼-값 쌍
developer) ;; 심볼을 통해 값 호출
;=> "Alice in Wonderland"
;; 기존 developer의 바인딩이 그대로 존재
developer ;; 심볼을 통해 값 호출
;=> "Alice"
;; let 안에서 일어나는 일은 let 안에서만 유효
(let [developer "Alice in Wonderland" ;; 심볼-값 쌍
rabbit "White Rabbit"] ;; 심볼-값 쌍
[developer rabbit]) ;; 심볼을 통해 값 호출
;=> ["Alice in Wonderland" "White Rabbit"]
;; 따라서 let 안에서 바인딩한 심볼을 let 바깥에서 참조하게 되면 에러가 발생
rabbit ;; 심볼을 통해 값 호출 시도
;=> CompilerException java.lang.RuntimeException:
; Unable to resolve symbol: rabbit in this context- 심볼과 바인딩
- 전역
var를 만들기 위해def를 사용한다. - 지역 바인딩을 만들기 위해
let을 사용한다.
- 전역
- 함수 만들기는 클로저 프로그램의 가장 일반적이고 중요한 부분 중 하나이다.
- 함수를 만들고 그것에 심볼을 할당하고 나중에 그 함수를 호출할 수 있다.
-
defn은def와 비슷하지만, 함수를 위한var를 만든다. -
defn은 함수 이름, 함수 인수들의 벡터, 함수 본문을 인수로 받는다. - 함수를 호출하려면 함수를 괄호로 둘러싸서 사용하면 된다.
- 함수를 호출하면 클로저는 그 함수를 평가한 후 결과를 반환한다.
;; 빈 벡터를 이용하면 함수를 인수 없이 정의 가능
(defn follow-the-rabbit [] "Off we go!")
;=> #'user/follow-the-rabbit ;; 함수에 대한 심볼 var 객체가 생성됨
(follow-the-rabbit) ;; 심볼을 통해 함수 호출
;=> "Off we go!"
;; 인수를 받는 함수 정의(생성)
(defn shop-for-jams [jam1 jam2]
{:name "jam-basket"
:jam1 jam1
:jam2 jam2})
;=> #'user/shop-for-jams ;; 함수에 대한 심볼 var 객체가 생성됨
(shop-for-jams "strawberry" "marmalade") ;; 심볼을 통해 함수에 인수를 넣어 호출
;=> {:name "jam-basket", :jam1 "strawberry", :jam2 "marmalade"}- 무명(익명) 함수 : 함수에 이름을 붙이지 않고 간단히 사용하고 싶은 경우
fn연산자로 만든다.-
fn은 인수들의 벡터와 함수 본문을 받는다. - 무명(익명) 함수도 역시 괄호로 함수를 둘러싸서 호출하면 된다.
-
fn은def로 무명 함수에 이름을 바인딩하는 것과 동일하다.
-
;; 무명(익명) 함수가 생성됨 == 함수를 반환
(fn [] (str "Off we go" "!"))
;=> #object[user$eval1271$fn__1272 0x45b23d29 "user$eval1271$fn__1272@45b23d29"]
;; 생성된 무명(익명) 함수를 괄호로 감싸서 호출
((fn [] (str "Off we go" "!")))
;=> "Off we go!"
;; fn은 def로 무명(익명) 함수에 이름을 바인딩하는 것과 동일
(def follow-again (fn [] (str "Off we go" "!")))
;=> #'user/follow-again // 함수에 대한 심볼 var 객체가 생성됨
(follow-again) ;; 심볼을 통해 함수 호출
;=> "Off we go!"
;; 무명(익명) 함수를 만드는 단축형. 괄호 앞에 #을 붙임
(#(str "Off we go" "!"))
;=> "Off we go!"
;; 인수가 하나 있는 경우는 퍼센트 기호(%)로 나타냄
(#(str "Off we go" "!" " - " %) "again")
;=> "Off we go! - again"
;; 인수가 여러 개라면 퍼센트 기호에 숫자를 붙여 표시
(#(str "Off we go" "!" " - " %1 %2) "again" "?")
;=> "Off we go! - again?"-
심볼을 관리하는 방법
- 객체지향 언어에서는 비슷한 함수들을 묶어서 담는 객체를 사용한다.
- 클로저는 이름공간을 사용한다.
-
이름공간에서 심볼을 관리하기
- 이름공간은 var에 대한 접근을 조직하고 제어하는 방법이다.
;; 디폴트 이름공간을 alice.favfoods로 변경
(ns alice.favfoods)
;=> nil
*ns* ;; 현재 이름공간을 표시
;=> #object[clojure.lang.Namespace 0x79bac443 "alice.favfoods"]
(def fav-food "strawberry jam")
;=> #'alice.favfoods/fav-food ;; 디폴트 이름공간 user가 alice.favfoods로 변경
fav-food
;=> "strawberry jam"
alice.favfoods/fav-food
;=> "strawberry jam"
;; 다른 이름공간으로 전환하면 그 심볼은 더 이상 참조되지 않음
(ns rabbit.favfoods)
;=> nil
fav-food
;=> CompilerException java.lang.RuntimeException:
; Unable to resolve symbol: fav-food in this context
;; 전환한 다른 이름공간에서는 그 심볼을 다른 값을 지정하는 var로 정의 가능
(ns rabbit.favfoods)
(def fav-food "lettuce soup") ;; fav-food를 새로 정의
;=> #'rabbit.favfoods/fav-food
fav-food ;; rabbit.favfoods에 속한 fav-food
;=> "lettuce soup"
alice.favfoods/fav-food ;; alice.favfoods에 속한 fav-food
;=> "strawberry jam"- 클로저 라이브러리들은 이름공간과 그 이름공간의 심볼들로 구성된다.
-
require를 사용해서 자신의 이름공간에서 라이브러리를 사용할 수 있는 세 가지 방법이 있다.- 이름공간을 인수로 받아
require를 사용 -
:as를 사용해서require의 별칭 기능을 이용 -
require를 이름공간,:refer :all옵션과 함께 사용하면 이름공간의 모든 심볼이 로딩되고, - 현재의 이름공간에서 심볼 이름만으로 직접 접근할 수 있다. 이 경우 이름 충돌이 발생할 수 있다.
- 이름공간을 인수로 받아
;; 합집합 구하기
(clojure.set/union #{:r :b :w} #{:w :p :y})
;=> #{:y :r :w :b :p}
(require 'clojure.set) ;; 1. 이름공간을 인수로 받아 심볼 로딩
(ns wonderland) ;; 이름공간 전환
;=> nil
;; 별칭 사용하기
(require '[alice.favfoods :as af])
;= nil
af/fav-food ;; 2. 별칭으로 이름공간을 로딩해서 심볼 호출
;=> "strawberry jam"
;; 보통 ns 안에서 키워드와 벡터의 형태로 사용됨
(ns wonderland
(:require [alice.favfoods :as af]))
;=> nil
af/fav-food
;=> "strawberry jam"
;; 이름 충돌 발생 위험
(ns wonderland
(:require [alice.favfoods :refer :all] ;; 모든 심볼 로딩
[rabbit.favfoods :refer :all])) ;; 모든 심볼 로딩
;=> IllegalStateException fav-food already refers to:
; #'alice.favfoods/fav-food' in namespace: wonderland- 대부분의 클로저 코드는
require로 라이브러리르 사용하고:as로 별칭을 지정한다. - 테스트 코드 작성할 때는 테스트하려는 이름공간과
clojure.test의 함수들을 직접 사용하는 것이 일반적이다. -
use함수는require를:refer :all과 함께 쓰는 것과 같지만 전자보다 후자가 더 좋다(?)
- 데이터 컬렉션을 만들고 조작할 수 있다.
- 함수를 만들 수 있다.
- 심볼을 만들 수 있다.
- 이름공간으로 코드를 관리할 수 있다.
;; 1. clojure.set 이름공간을 로딩하고 s라는 별칭을 만들어 사용
;; 2. food1의 집합은 food-set1 심볼에 바인딩
;; 3. food2의 집합은 food-set2 심볼에 바인딩
;; 4. clojure.set 이름공간의 intersection 함수와 심볼 food-set1, food-set2를 사용
(ns wonderland
(:require [clojure.set :as s])) ;; 1
(defn common-fav-foods [foods1 foods2]
(let [food-set1 (set foods1) ;; 2
food-set2 (set foods2) ;; 3
common-foods (s/intersection food-set1 food-set2)] ;; 4
(str "Common Foods: " common-foods)))
(common-fav-foods [:jam :brownies :toast]
[:lettuce :carrots :jam])
;=> "Common Foods: #{:jam}" ;; 교집합 결과 jam 반환