Skip to content

3. Dipping Your toes in the pool

Joony.L edited this page Nov 3, 2014 · 8 revisions

Truthiness

  • 사전적 의미: 진실성 부여(사실과는 상관없이 사람들이 진실이기를 바라는 마음에서 부여하는 진실성)

What's truth?

  • Every object is “true” all the time, unless it’s nil or false.

    (if true :truthy :falsey)  ;=> :truthy
    (if [] :truthy :falsey)    ;=> :truthy
    (if nil :truthy :falsey)   ;=> :falsey
    (if false :truthy :falsey) ;=> :falsey

Don't create Boolean objects

  • NEVER do this

    (def evil-false (Boolean. "false")) 
    
    evil-false
    ;=> false
    
    (= false evil-false)
    ;=> true
          
    (if evil-false :truthy :falsey)
    ;=> :truthy
  • right way

    (if (Boolean/valueOf "false") :truthy :falsey)
    ;=> :falsey

nil vs false

  • Rarely do you need to differentiate between the two non-truthy values, but if you do, you can use nil? and false?

    (when (nil? nil) "Actually nil, not false")
    ;=> "Actually nil, not false"
  • Keeping in mind the basic rule that everything in Clojure is truthy unless it’s false or nil is an astonishingly powerful concept, allowing for elegant solutions.

Nil pun with care

  • The seq function returns a sequence view of a collection, or nil if the collection is empty.

    (seq [1 2 3])
    ;=> (1 2 3)
     
    (seq [])
    ;=> nil
  • print-seq function

    (defn print-seq [s]
      (when (seq s) B Check for empty
        (prn (first s))
        (recur (rest s))))
    
    (print-seq [])
    ;=> nil
    
    (print-seq [1 2])
    ;1
    ;2
    ;=> nil

Destructuring

(assignment 할 때 받아들일 것만 선택하라.)Your assignment, should you choose to accept it

벡터를 쓸 때의 디스트럭처링

맵 쓸 때의 디스트럭처링

함수 인자안에서의 디스트럭처링

디스트럭처링 vs accessor메소드

많은 객체 지향 언어에서, 너는 아마 너의 앱 데이타 객체를 관리하기 위해,각각의 게터세터 메소드의 집합과 함께 새 클래스를 만들었을 것이다. 클로저는 대신에 지어놨다 편리하게 맵과 벡터를 사용할수 있게 추상화를 하는데에. 이거는 디스트럭처링을 자연스럽고 직관적이게 한다. 너가

실험을 위해 REPL 사용하기

Clojure REPL을 사용하면 처음 사용해보는 라이브러리도 즐거운 마음으로 확인해볼 수 있다.

시퀀스를 가지고 실험하기

  • 화면에 픽셀을 표현하는 x, y 좌표를 가지는 시퀀스와 xor 연산을 해서 재미난 이미지를 만들어 보자

  • 먼저 range 라는 함수를 가지고 좌표를 표현해보기 위해 range가 어떻게 동작하는지 확인해본다.

     (range 5)
    ;=> (0 1 2 3 4)
  • x, y 좌표 쌍을 만들기 위해서 for를 사용해서 원하는 결과가 나오는지 실험해보자.

    (for [x (range 2) y (range 2)] [x y])
    ;=> ([0 0] [0 1] [1 0] [1 1])
  • xor 연산을 위해서 xor 함수같은 것이 있을 것으로 예상하고 REPL로 실험해보자.

    (xor 1 2)
    ;=> java.lang.Exception: Unable to resolve symbol: xor in this context
  • xor라는 이름의 함수는 없다는 것을 알았다. 같은 기능을 하는 함수가 없는지 find-doc으로 찾아본다.

    (find-doc "xor")
    ; -------------------------
    ; clojure.core/bit-xor
    ; ([x y]) ([x y & more])
    ;   Bitwise exclusive or
    ;=> nil
  • bit-xor라는 함수가 같은 기능을 함수라는 것을 알게되었고 REPL에서 확인해보자.

    (bit-xor 1 2)
    ;=> 3
  • 이제 먼저 만들었던 x, y 쌍을 주는 for문에 xor 값을 추가해보자. REPL에서 UP키를 누르면 이전에 실행했던 구문을 불러올 수 있다.

    (for [x (range 2) y (range 2)]
      [x y (bit-xor x y)])
    ;=> ([0 0 0] [0 1 1] [1 0 1] [1 1 0])
  • 잘 된것 같으니 이제까지 만들었던 것을 나중에 사용하기 쉽게 함수로 만들자.

    (defn xors [max-x max-y]
      (for [x (range max-x) y (range max-y)]
           [x y (bit-xor x y)]))
    
    (xors 2 2)
    ;=> ([0 0 0] [0 1 1] [1 0 1] [1 1 0])
  • 위의 함수를 Clojure 소스파일 확장자인 .clj로 저장해두면 좋을것 같다. 또는 ClojureScript 파일인 .cljs로 저장해두자.

그래픽스를 가지고 실험하기

  • Clojure REPL은 자바 라이브러리를 실험하기에 좋은 환경이다. java.awt의 Frame을 가지고 시작해보자.

    (def frame (java.awt.Frame.))
    ;=> #'user/frame
  • Frame이 생성되었는데 볼수가 없다. 왜 그런지 frame의 상태를 확인해보자.

    frame
    ;=> #<Frame java.awt.Frame[frame0,0,22,0x0,invalid,hidden,...]>
  • 뭔가 hidden이라는 것이 보이는 걸로 보아 이것을 바꿔줄 수 있는 메서드가 있을 것 같다. Frame의 모든 메서드 중에서 Vis어쩌고로 시작되는 메서드를 찾아보자.

    (for [meth (.getMethods java.awt.Frame)
        :let [name (.getName meth)]
        :when (re-find #"Vis" name)]
    name)
    ;=> ("setVisible" "isVisible")
  • 관련된 메서드가 두개 보인다. 두개다 REPL에서 확인해보자.

    (.isVisible frame)
    ;=> false
    
    (.setVisible frame true)
    ;=> nil
  • setVisible을 했는데도 화면에 나타자지 않는다. 아까 frame의 속성을 봤을 때 크기로 보이는 값이 0,0 이였던것을 기억해보면 크기가 지정되지 않아 화면에 안보인것 같다.

    (.setSize frame (java.awt.Dimension. 200 200))
    ;=> nil
  • 이제 화면에 나왔다. 팁으로 (javadoc frame)을 하면 자바 문서가 브라우저 창으로 나온다.

  • 화면에 뭘 그리기 위해서 그래픽 컨택스트를 가져와 보자.

    (def gfx (.getGraphics frame))
    ;=> #'user/gfx
  • 화면에 뭘 그려보자.

    (.fillRect gfx 100 100 50 75)
  • 화면에 검은 사각형이 나왔다. 재미있지 않는가? 이제 오랜지색 사각형을 그려보자.

    (.setColor gfx (java.awt.Color. 255 128 0))
    (.fillRect gfx 100 150 75 50)
  • 다른 여러가지도 REPL에서 실험해볼 수 있다.

함께쓰기

  • 앞에서 만들어둔 함수를 써서 재미난 이미지를 만들어 보자.

    (doseq [[x y xor] (xors 200 200)]
     (.setColor gfx (java.awt.Color. xor xor xor))
     (.fillRect gfx x y 1 1))
  • xors 함수에서 나오는 시퀀스의 백터 항목을 x, y, xor 값에 디스트럭처링해서 색상으로 지정하고 전체 픽셀에 그려주는 예제이다.

뭔가 잘못되었을 때

  • 패턴이 중간에 짤리는 것을 막기위해서 이미지 범위를 더 크게 설정해보자.

     (doseq [[x y xor] (xors 500 500)]
     (.setColor gfx (java.awt.Color. xor xor xor))
     (.fillRect gfx x y 1 1))
    ; java.lang.IllegalArgumentException:
    ;    Color parameter outside of expected range: Red Green Blue
  • 뭔가 잘 못되었다. REPL에서는 예외가 발생했을 때 특별한 var인 *e에 예외가 저장된다. 예외 상세 내용을 보기 위해 아래와 같이 해보자.

     (.printStackTrace *e)
     ; java.lang.IllegalArgumentException: Color parameter outside of
     ; expected range: Red Green Blue
     ; at clojure.lang.Compiler.eval(Compiler.java:4639)
     ; at clojure.core$eval__5182.invoke(core.clj:1966)
     ; at clojure.main$repl__7283$read_eval_print__7295.invoke(main.clj:180) ; ...skipping a bit here...
     ; Caused by: java.lang.IllegalArgumentException: Color parameter
     ;         outside of expected range: Red Green Blue
     ; at java.awt.Color.testColorValueRange(Color.java:298)
     ; at java.awt.Color.<init>(Color.java:382)
     ; ...skipping a bit more...
     ; ... 11 more
     ;=> nil
  • 뭔가 긴글이 나왔다고 당황하지 말고 천천히 살펴보자. 뭔저 caused를 기준으로 크게 두 부분으로 볼 수 있고 두번째 부분인 Caused 다음에 나오는 문장 부터 보자.

    at java.awt.Color.testColorValueRange(Color.java:298)
  • java의 stack trace는 아래와 같이 구성된다.

     at <class>.<method or constructor>(<filename>:<line>)
  • 이 경우에는 testColorValueRange 함수에서 에러가 발생했고 다음 라인을 보면 Color의 생성자에서 에러가 난것을 확인 할 수 있다. 뭔가 값을 초기화할 때 잘 못된것 같다. 이것을 고쳐보자.

    (defn xors [xs ys]
     (for [x (range xs) y (range ys)]
       [x y (rem (bit-xor x y) 256)]))
  • bit-xor의 결과를 그대로 사용하지 않고 256의 나머지 값으로 바꿧다.

  • 다시 확인해보기 위해서 기존의 이미지를 지우는 함수를 하나 만들자.

     (defn clear [g] (.clearRect g 0 0 200 200))

재미로

  • bit-xor 함수를 사용해서 재미있는 이미지를 만들어봤는데 다른 연산자를 사용해보면 또 다른 이미지를 얻을 수 있다. 그래서 아래와 같이 연산자를 파라미터로 받을 수 있는 함수를 만들어 보자.

     (defn f-values [f xs ys]
     (for [x (range xs) y (range ys)]
     		[x y (rem (f x y) 256)]))
    
     (defn draw-values [f xs ys]
     	(clear gfx)
     	(.setSize frame (java.awt.Dimension. xs ys))
     	(doseq [[x y v] (f-values f xs ys)]
     	  (.setColor gfx (java.awt.Color. v v v))
     	  (.fillRect gfx x y 1 1)))
    
     (draw-values bit-and 256 256)
    (draw-values + 256 256)
    (draw-values * 256 256)
     ``
    
  • REPL을 가지고 Clojure를 개발하면 사용하려는 함수를 미리 실험해봄으로 개발 시간을 더 줄 일 수 있다.