Clojure에서 println 어렵게 해보기

 

lamda 함수 및 "->" 연산자의 작동 방식 이해를 위해

;; 1)
(println "hello")

;; 2)
((fn [str] (println str)) "hello")

;; 3)
(-> "hello" (println))

;; 4)
(-> "hello" ((fn [str] (println str))))
,
Clojure에서 키워드는 앞에 :이 붙은 요소인데, 설명을 읽어봐도 잘 이해가 되지 않으므로 사용용도를 통해 그 정체를 기억하자.


맵의 이름은 함수처럼 사용해서 데이터를 참조하는 것이 가능한데, 키워드를 키로 지정할 경우, 이 키워드도 함수처럼 사용하는 것이 가능하다. (다른 맵 간에 키워드 중복 사용도 무방)



맵 정의

(def planet {:name "earth" :type "terrestrial"
        :satelite {:sateliteName "moon" :alternateName "luna"}})


데이터 참조

; 아래 세 구문 모두 같은 값 "terrestrial"을 갖는다.

(planet :type)
(get planet :type)
(:type planet)  ; 만약 키가 "type"(문자열)이라면, 이런 식의 사용은 불가능하다.

-> "terrestrial"


연쇄 참조

; 아래 세 구문 모두 같은 값 "luna"을 갖는다.

(:alternateName (:satelite planet))
(-> planet :satelite :alternateName)
(get-in planet [:satelite :alternateName])

-> "luna"


키가 존재하지 않을 경우

; 존재하지 않는 키인 키워드를 사용할 경우 nil을 반환한다.

(:orbit planet)

-> nil


키워드에 네임스페이스 사용

; / 기호를 사용해 키워드에 네임스페이스를 줄 수도 있다.

(:etc/alternateName {:name/sateliteName "moon" :etc/alternateName "luna"})

-> "luna"


<테스트 환경>
- OS : Windows 10
- Leiningen 버전 : 1.0.0
,

Clojure 활용의 핵심! 자바 클래스 사용방법에 대해 정리해보았습니다.

 

 

클래스 import

; 풀네임으로 쓰는 방법
java.util.Date  ; => java.util.Date


; 줄여서 쓰는 방법
(import java.util.Date)
Date  ; => java.util.Date

 

자동 import

; java.lang 밑에 있는 클래스들은 별도의 import 없이 줄여쓰는 방법으로 쓸 수 있다
Math  ; => java.lang.Math
String  => java.lang.String

 

이너(Inner) 클래스 import

; 풀네임으로 쓰는 방법
java.util.Map$Entry  ; => java.util.Map$Entry


; 줄여서 쓰는 방법
(import java.util.Map$Entry)
Map$Entry  ; => java.util.Map$Entry

 

인스턴스 생성

; new 구문을 사용
(new java.util.Date)

; 약식 구문
(java.util.Date.)

; 약식 구문 (import 후)
(import java.util.Date)
(Date.)

; 생성자 파라미터 전달하는 경우
(java.net.URI. "http://clojure.org")

 

인스턴스 메소드 호출

; . 구문을 사용
(let [d (java.util.Date.)]
  (. d getTime))

; 약식 구문
(let [d (java.util.Date.)]
  (.getTime d))

 

정적 메소드 호출

; . 구문을 사용
(. Math floor 5.677)

; 약식 구문
(Math/floor 5.677)

 

연쇄 호출

(.. (java.util.Date.) getTime toString)  ; (-> (java.util.Date.) (. getTime) (. toString)) 과 동일
                                         ; (-> (java.util.Date.) (.getTime) (.toString)) 과도 동일

 

동일 오브젝트에 복수 호출

(doto (java.util.Stack.)
  (.push 42)
  (.push 13)
  (.push 7))  ; 첫번째 파라미터의 오브젝트가 나머지 호출의 첫번째 파라미터로 들어간다

; 아래와 같이 써도 동일
(doto (java.util.Stack.)
  (. push 42)
  (. push 13)
  (. push 7))

 

자바 필드(멤버 변수) 읽기

; . 구문을 사용
(let [pt (java.awt.Point. 10 20)]
(. pt x))  ; pt.x를 읽음

; 약식 구문
(let [pt (java.awt.Point. 10 20)]
(.x pt))

 

자바 필드(멤버 변수) 쓰기

(let [pt (java.awt.Point. 10 20)]
  (set! (.y pt) 100))  ; pt.y -> 100으로 세팅됨

 

Enum 타입 사용

java.util.concurrent.TimeUnit/MILLISECONDS

 

상수/인스턴스의 자바 클래스 얻기

(class 1)       ; => java.lang.Long
(class "docs")  ; => java.lang.String
(class (java.net.URI. "http://github.com"))  ; => java.net.URI

 

클래스명으로 클래스 얻기

(Class/forName "java.util.Date")  ; => java.util.Date

 

참고: http://clojure-doc.org/articles/language/interop.html

<테스트 환경>
- OS : Windows 10
- Leiningen 버전 : 1.0.0

 

,

버튼 오른쪽 아래로 정렬하는 예제입니다. 아래 Launcher Template 예제의 MainWindow.__init__() 부분에 교체하여 테스트할 수 있습니다.

vbox = wx.BoxSizer(wx.VERTICAL)

button = wx.Button(self, id = 108, label = "TITLE_8", size = (120, 50))
hbox = wx.BoxSizer(wx.HORIZONTAL)
hbox.AddStretchSpacer()
hbox.Add(button)
hbox.AddSpacer(10) # optional

self.Bind(wx.EVT_BUTTON, self.OnButton, button)

vbox.AddStretchSpacer()
vbox.Add(hbox, 0, wx.EXPAND | wx.ALL)
vbox.AddSpacer(10) # optional

self.SetSizer(vbox, True)
self.Layout()

<테스트 환경>
OS : Windows 10
Python 버전 : 2.7
wxPython 버전 : 2.8.12.1
,

???? - 5. 아두이노에서 지자기 센서(GY-271) 사용하기


간단히 말하면 나침반 역할을 하는 센서입니다.

칩셋이 원래는 HMC5883L이었으나 단종되어 최근에는 QMC5883L로 나오고 있는듯합니다. 하지만 보유중인 부품이 HMC5883L인 관계로 해당 칩셋 기준으로 테스트를 진행하겠습니다.



배선도



관련 라이브러리 설치

  1. 구글에서 HMC5883L arduino library를 검색합니다.
  2. 나온 결과 중에서 https://github.com/jarzebski/Arduino-HMC5883L 로 들어갑니다.
  3. 라이브러리를 ZIP 파일로 다운받습니다. (CODE 버튼 누르고, Download ZIP 선택)
  4. 아두이노 IDE를 실행
  5. 스케치 > 라이브러리 포함하기 > .ZIP 라이브러리 추가 메뉴를 실행합니다.
  6. 다운 받은 ZIP 파일을 선택합니다.


예제 실행

  1. 파일 >예제 밑에 Arduino-HMC5883L-master 항목이 생긴 것을 볼 수 있습니다.
  2. 이 밑에 있는 HMC5883L_processing 예제를 열어서 컴파일 후 업로드합니다.
  3. 툴 > 시리얼 모니터를 엽니다.


실행 결과

  • 아래와 같이 노멀 벡터 X/Y/Z 요소, 보정 안된 방위 각도, 보정(HMC5883L 각도 이슈)된 방위 각도, 평활화(smooth) 처리된 방위각 순으로 출력되는 것을 확인할 수 있습니다.
1117.80:-376.28:-92.00:345.83:337.00:339
1123.32:-380.88:-82.80:345.70:337.00:339
1116.88:-369.84:-89.24:346.11:339.00:339
1127.92:-387.32:-73.60:345.48:337.00:339
1120.56:-371.68:-84.64:346.08:339.00:339
1139.88:-386.40:-72.68:345.71:337.00:339
1121.48:-371.68:-84.64:346.10:339.00:339
1139.88:-386.40:-70.84:345.71:337.00:339
1130.68:-385.48:-73.60:345.61:337.00:339
1138.96:-387.32:-70.84:345.65:337.00:339
1129.76:-387.32:-72.68:345.51:337.00:339
1138.96:-387.32:-69.00:345.65:337.00:339
1129.76:-386.40:-69.92:345.55:337.00:339
1122.40:-373.52:-80.96:346.03:339.00:339
1132.52:-387.32:-69.92:345.55:337.00:339
1123.32:-373.52:-81.88:346.04:339.00:339
1141.72:-388.24:-66.24:345.65:337.00:339
1123.32:-372.60:-81.88:346.08:339.00:339
1141.72:-388.24:-68.08:345.65:337.00:339

,

???? - 4. 아두이노에서 기울기 센서(MPU-6050) 사용하기


MPU-6050은 아두이노에서 사용할 수 있는 기울기 센서 중 하나입니다. MPU-6050은 (중력)가속도 3축 + 자이로 3축을 합쳐서 6축 센서라고 불리는데 가속도 센서는 정적인 상태에서만 정확한 측정이 가능하므로 자이로 센서로 이를 보정하는 식으로 측정이 이루어집니다. (자이로 3축 + 가속도 2축 + 온도 이렇게 6축이라는데도 있습니다) 기능으로는 Pitch, Roll, Yaw 세 방향의 기울기를 측정할 수 있는데, 실제로 테스트를 해봤을 때 Yaw값의 경우는 다소 정확하지 않은 느낌을 받았습니다.



배선도



관련 라이브러리 설치

  1. 아두이노 IDE를 실행합니다.
  2. 툴 -> 라이브러리 관리 메뉴를 실행합니다.
  3. 라이브러리 매니저 화면에서 MPU6050를 검색합니다.
  4. MPU6050 by Electronic Cats를 설치합니다.


예제 실행

  1. 파일 >예제 밑에 MPU6050 항목이 생긴 것을 볼 수 있습니다.
  2. 이 밑에 있는 MPU6050_DMP6 예제를 열어서 컴파일 후 업로드합니다.
  3. 툴 > 시리얼 모니터를 엽니다.
  4. 속도를 115200 보드레이트로 변경합니다.
  5. 시리얼 모니터 상단 입력란에 아무 값이나 입력하고 전송을 누르면 화면 출력이 시작됩니다.


실행 결과

  • 센서를 기울임에 따라 Yaw, Pitch, Roll 값이 변경되는 것을 확인할 수 있습니다.
Initializing DMP...
Enabling DMP...
Enabling interrupt detection (Arduino external interrupt 0)...
DMP ready! Waiting for first interrupt...

ypr	30.79	18.42	-64.17
ypr	31.31	18.09	-63.87
ypr	31.79	18.02	-63.55
ypr	32.53	18.19	-62.95
ypr	33.78	19.00	-61.67
ypr	35.42	20.35	-59.94
ypr	37.08	21.77	-58.21
ypr	38.17	22.89	-57.00
ypr	38.77	23.71	-56.29
ypr	39.22	24.47	-55.71
ypr	39.75	25.41	-54.93
ypr	40.41	26.68	-53.86
ypr	41.32	28.44	-52.36
ypr	42.19	30.52	-50.78
ypr	42.77	32.47	-49.49
ypr	43.36	34.06	-48.33
ypr	43.91	35.41	-47.32
ypr	44.24	36.57	-46.51
ypr	44.41	37.62	-45.85
ypr	44.76	38.83	-45.03
ypr	45.24	40.09	-44.09
ypr	45.65	41.30	-43.15
ypr	46.17	42.65	-42.10
ypr	46.70	43.95	-41.17
ypr	46.98	44.78	-40.65
ypr	46.97	45.16	-40.28

,

???? - 3. 누를 것이 필요해! (내부풀업 사용)

 

보통은 스위치에 풀다운 저항을 붙여서 많이 사용하는데, 여기서는 귀차니즘에 굴복하라!는 일일과제 달성을 위하여 내부 풀업 저항을 이용한 방법을 확인해보겠습니다. 이 경우, 출력값이 버튼을 누르지 않았을 때 HIGH, 눌렀을 경우에 LOW로 들어오게 됩니다.

 

 

배선도

 

 

소스

void setup() {
  pinMode(4, INPUT_PULLUP);
}

void loop() {
  int buttonD4 = digitalRead(4);

  if (buttonD4 == LOW) {
    Serial.println("button pressed!");
  }
}

 

실행 결과

  • 위 소스를 테스트해보면 위에서 말한대로 입력이 들어오는 것을 확인할 수 있다.
  • 하지만 실사용의 경우에는 아래와 같이 previous 값 관리를 해야 버튼이 한번 눌릴 때 이벤트 처리가 여러번 되는 것을 방지할 수 있다.
void setup() {
  pinMode(4, INPUT_PULLUP);
}

int buttonD4Prev = HIGH;

void loop() {
  int buttonD4 = digitalRead(4);

  if (buttonD4Prev == HIGH && buttonD4 == LOW) {
    Serial.println("button pressed!");
  }
  buttonD4Prev = buttonD4;
}

 

,

???? - 2. 돌릴 것이 필요해! (포텐셔미터)

 

흔히 가변저항으로 알려진 포텐셔미터의 사용법을 확인해보겠습니다.

 

 

배선도

 

 

소스

void setup() {
  Serial.begin(9600);
}

void loop() {
  int sensorValue = analogRead(A0);
  Serial.println(sensorValue);
}

 

실행 결과

  • 시리얼 모니터를 통해 결과를 확인해보면 포텐셔미터를 돌림에 따라 0 ~ 1023 사이의 값이 출력되는 것을 확인할 수 있다.

 

,