리터럴 옥탈
어느 시점에서 나는 적절한 행과 열을 유지하기 위해 선행 0을 사용하는 행렬을 읽었습니다. 선행 0은 분명히 기본 값을 변경하지 않기 때문에 수학적으로 정확합니다. 그러나이 행렬로 var를 정의하려는 시도는 다음과 같이 신비하게 실패합니다.
java.lang.NumberFormatException: Invalid number: 08
나를 완전히 당황하게했다. 그 이유는 Clojure가 선행 0이있는 리터럴 정수 값을 8 진법으로 취급하고 8 진법에는 숫자 08이 없기 때문입니다.
Clojure는 0x 접두사 를 통해 전통적인 Java 16 진수 값을 지원한다는 점도 언급해야 합니다. 42 진법 10 인 2r101010 또는 36r16 과 같이 "base + r + value"표기법을 사용하여 2와 36 사이의 기수를 사용할 수도 있습니다 .
익명 함수 리터럴에서 리터럴 반환 시도
이것은 작동합니다 :
user> (defn foo [key val]
{key val})
#'user/foo
user> (foo :a 1)
{:a 1}
그래서 이것도 효과가 있다고 믿었습니다.
(#({%1 %2}) :a 1)
그러나 다음과 함께 실패합니다.
java.lang.IllegalArgumentException: Wrong number of args passed to: PersistentArrayMap
때문에 # () 리더 매크로로 확대됩니다
(fn [%1 %2] ({%1 %2}))
지도 리터럴을 괄호로 묶습니다. 첫 번째 요소이기 때문에 함수로 처리되지만 (실제로는 리터럴 맵) 필수 인수 (예 : 키)가 제공되지 않습니다. 요약하면 익명 함수 리터럴은 다음으로 확장 되지 않습니다.
(fn [%1 %2] {%1 %2}) ; notice the lack of parenthesis
따라서 익명 함수의 본문으로 리터럴 값 ([], : a, 4, %)을 가질 수 없습니다.
주석에는 두 가지 해결책이 있습니다. Brian Carper 는 다음과 같이 시퀀스 구현 생성자 (array-map, hash-set, vector)를 사용할 것을 제안합니다.
(#(array-map %1 %2) :a 1)
반면 댄 쇼 당신은 사용할 수있는 신원 외부 괄호 랩을 해제하는 기능 :
(#(identity {%1 %2}) :a 1)
Brian의 제안은 실제로 나를 다음 실수로 인도합니다.
해시 맵 또는 배열 맵 이 변하지 않는 구체적인 맵 구현을 결정 한다고 생각
다음을 고려하세요:
user> (class (hash-map))
clojure.lang.PersistentArrayMap
user> (class (hash-map :a 1))
clojure.lang.PersistentHashMap
user> (class (assoc (apply array-map (range 2000)) :a :1))
clojure.lang.PersistentHashMap
같은 - 당신은 일반적으로 Clojure의지도의 구체적인 구현에 대해 걱정할 필요가 없지만, 당신은지도 성장 기능을 알고 있어야합니다 ASSOC 또는 접속사가 - 테이크 수 PersistentArrayMap을 a와 반환 PersistentHashMap을 더 큰지도를 위해 어떤 수행 빠르고.
초기 바인딩을 제공하기 위해 루프 대신 함수를 재귀 지점으로 사용
처음 시작할 때 다음과 같은 많은 함수를 작성했습니다.
; Project Euler #3
(defn p3
([] (p3 775147 600851475143 3))
([i n times]
(if (and (divides? i n) (fast-prime? i times)) i
(recur (dec i) n times))))
실제로 루프 가이 특정 함수에 대해 더 간결하고 관용적 일 때 :
; Elapsed time: 387 msecs
(defn p3 [] {:post [(= % 6857)]}
(loop [i 775147 n 600851475143 times 3]
(if (and (divides? i n) (fast-prime? i times)) i
(recur (dec i) n times))))
빈 인수 인 "기본 생성자"함수 본문 (p3 775147 600851475143 3) 을 루프 + 초기 바인딩으로 대체했습니다 . RECUR는 지금 (대신 FN 매개 변수) 루프 바인딩을 리 바인드하고 (대신 FN의 루프) 다시 재귀 지점으로 이동합니다.
"가상"변수 참조
탐색 프로그래밍 중에 REPL을 사용하여 정의 할 수있는 var의 유형에 대해 말하고 있습니다. 그런 다음 무의식적으로 소스에서 참조합니다. 네임 스페이스를 다시로드하고 (아마도 편집기를 닫아서) 나중에 코드 전체에서 참조 된 언 바운드 기호를 발견 할 때까지 모든 것이 잘 작동합니다. 이것은 또한 한 네임 스페이스에서 다른 네임 스페이스로 var를 이동하면서 리팩토링 할 때 자주 발생합니다.
for 목록 이해력을 명령형 for 루프처럼 다루기
기본적으로 단순히 제어 된 루프를 수행하는 것이 아니라 기존 목록을 기반으로 지연 목록을 만듭니다. Clojure의 doseq 는 실제로 명령형 foreach 루프 구조와 더 유사합니다.
그들이 어떻게 다른지에 대한 한 가지 예는 임의의 술어를 사용하여 반복되는 요소를 필터링하는 기능입니다.
user> (for [n '(1 2 3 4) :when (even? n)] n)
(2 4)
user> (for [n '(4 3 2 1) :while (even? n)] n)
(4)
다른 점은 무한 지연 시퀀스에서 작동 할 수 있다는 것입니다.
user> (take 5 (for [x (iterate inc 0) :when (> (* x x) 3)] (* 2 x)))
(4 6 8 10 12)
또한 둘 이상의 바인딩 표현식을 처리 할 수 있습니다. 맨 오른쪽 표현식을 먼저 반복하고 왼쪽으로 작업합니다.
user> (for [x '(1 2 3) y '(\a \b \c)] (str x y))
("1a" "1b" "1c" "2a" "2b" "2c" "3a" "3b" "3c")
또한 휴식이 없거나 조기 종료를 계속 합니다.
구조체 남용
나는 OOPish 배경에서 왔기 때문에 Clojure를 시작했을 때 내 두뇌는 여전히 객체 측면에서 생각하고있었습니다. "멤버"의 그룹화가 느슨하더라도 편안하게 느껴졌 기 때문에 모든 것을 구조체 로 모델링 했습니다. 실제로 구조체 는 대부분 최적화로 간주되어야합니다. Clojure는 메모리를 절약하기 위해 키와 일부 조회 정보를 공유합니다. 접근 자를 정의 하여 키 조회 프로세스의 속도 를 높이면 추가로 최적화 할 수 있습니다 .
전반적으로 당신이 사용하는 어떤 이득도 얻을 수없는 구조체를 넘는 맵 추가 복잡성이 가치가되지 않을 수도 있습니다, 성능을 제외하고.
승인되지 않은 BigDecimal 생성자 사용
나는 많은 BigDecimals가 필요했고 다음과 같은 추악한 코드를 작성했습니다.
(let [foo (BigDecimal. "1") bar (BigDecimal. "42.42") baz (BigDecimal. "24.24")]
실제로 Clojure가 숫자에 M 을 추가하여 BigDecimal 리터럴을 지원할 때 :
(= (BigDecimal. "42.42") 42.42M) ; true
설탕을 넣은 버전을 사용하면 부풀음이 많이 제거됩니다. 주석에서 twils 는 bigdec 및 bigint 함수를 사용하여 더 명확하면서도 간결하게 유지할 수 있다고 언급했습니다 .
네임 스페이스에 대한 Java 패키지 이름 지정 변환 사용
이것은 사실 그 자체로 실수가 아니라 전형적인 Clojure 프로젝트의 관용적 구조와 이름에 반하는 것입니다. 내 첫 번째 실질적인 Clojure 프로젝트에는 다음과 같은 네임 스페이스 선언과 해당 폴더 구조가 있습니다.
(ns com.14clouds.myapp.repository)
내 정규화 된 함수 참조가 부풀려졌습니다.
(com.14clouds.myapp.repository/load-by-name "foo")
더 복잡하게 만들기 위해 표준 Maven 디렉토리 구조를 사용했습니다 .
|-- src/
| |-- main/
| | |-- java/
| | |-- clojure/
| | |-- resources/
| |-- test/
...
다음의 "표준"Clojure 구조보다 더 복잡합니다.
|-- src/
|-- test/
|-- resources/
이것은 Leiningen 프로젝트와 Clojure 자체 의 기본값입니다 .
맵은 키 매칭을 위해 Clojure의 = 대신 Java의 equals ()를 사용합니다.
원래에 의해보고 chouser 에 IRC , 자바의 사용 등호 () 일부 직관적 결과를 리드 :
user> (= (int 1) (long 1))
true
user> ({(int 1) :found} (int 1) :not-found)
:found
user> ({(int 1) :found} (long 1) :not-found)
:not-found
Integer 및 Long 인스턴스 1은 모두 기본적으로 동일하게 인쇄되므로지도에서 값을 반환하지 않는 이유를 감지하기 어려울 수 있습니다. 이것은 당신이 모르는 사이에 long을 반환하는 함수를 통해 키를 전달할 때 특히 그렇습니다.
Clojure의 = 대신 Java의 equals ()를 사용하는 것은 맵이 java.util.Map 인터페이스를 준수하는 데 필수적 이라는 점에 유의해야합니다 .
저는 Stuart Halloway의 Programming Clojure , Luke VanderHart의 Practical Clojure , IRC 의 수많은 Clojure 해커의 도움과 메일 링리스트를 사용하여 제 답변을 돕고 있습니다.