Persistent and Transient Data Structures in Clojure
왕 이 클 라 우 드 커 뮤 니 티 를 방문 하여 더 많은 왕 이 기술 제품 의 운영 경험 을 알 아 보 는 것 을 환영 합 니 다.
최근 프로젝트 에 Transient 데이터 구 조 를 사 용 했 는데 이 데이터 구 조 를 사용 하면 프로그램의 집행 효율 이 어느 정도 향상 된다.Transient Data Stuctures 를 방금 접 했 습 니 다. 다음은 자신 이 그 에 대한 이 해 를 다음 과 같이 요약 합 니 다.
1. clojure 의 가 변 데이터 특성 및 저장 방식:
* 8195: 8195: clojure 의 데이터 구 조 는 가 변 적 이지 않 은 특성 (Persistent) 을 가진다. 즉, 하나의 데이터 구조 에 요 소 를 추가 하고 요 소 를 삭제 하 며 요 소 를 변경 하 며 새로운 데이터 구 조 를 되 돌려 주 는 것 이다. 원래 의 데이터 구 조 는 변 하지 않 는 다.
;;;
(def data [1 2 3 4 5])
;;=> #'user/data;; , ,
(update data 3 str)
;;=> [1 2 3 "4" 5]
data
;;=> [1 2 3 4 5]
;; , ,
(assoc data 5 6)
;;=> [1 2 3 4 5 6]
data
;;=> [1 2 3 4 5]
이상 은 data = [122345] 에 대해 증가 와 변경 작업 을 하고 data 데이터 자체 가 변 하지 않 고 새로운 데 이 터 를 생 성 했다. 이런 데 이 터 는 가 변성 이 데이터 의 안전성 에 매우 유리 하고 변경 대상 이 가 져 오 는 부작용 이 나타 나 지 않 는 다.이러한 특성 은 반드시 데이터 의 저장 방식 에 대해 높 은 요 구 를 가지 고 클 로 저 에서 의 데이터 구 조 는 아이디어 hash trees (http://lampwww.epfl.ch/papers/idealhashtrees.pdf) 저장 하기:
vector 의 모든 요 소 는 Leaf node 에 놓 여 있 습 니 다. Internal node 는 요 소 를 저장 하지 않 고 아들 노드 를 가리 키 는 지침 만 저장 합 니 다. 잎 노드 를 찾 는 데 사 용 됩 니 다. 그 중에서 Head 지침 은 나무의 뿌리 노드 를 가리 키 고 이 Head 지침 은 데 이 터 를 이 vector 의 크기 로 저장 합 니 다. vector 의 size 에 따라우 리 는 Internal node 를 따라 모든 번호 에 저 장 된 요 소 를 찾 을 수 있 습 니 다.『 8195 』 클 로 저 에서 의 vector 데이터 구 조 는 불변성 을 가지 기 때문에 복제 의 원 가 를 줄 이기 위해 클 로 저 는 저장 에 효율 적 인 공유 모델 을 취한 다.
;;
(def brown [0 1 2 3 4 5 6 7 8])
(def blue (assoc brown 5 'beef))
『 8195 』 위 에 brown 의 벡터 를 정의 한 다음 에 brown 의 여섯 번 째 요 소 를 바 꾸 어 새로운 벡터 blue 를 생 성 합 니 다. brown 과 blue 간 의 저장 구 조 는 다음 과 같 습 니 다.
위의 그림 에서 보 듯 이 brown 데이터 구조 에서 요 소 를 변경 한 후에 원래 의 brown 데이터 구 조 는 head 지침 부터 전혀 바 뀌 지 않 았 다. 모든 vector 는 자신의 head 지침 이 있 기 때문에 blue 는 반드시 자신의 head 지침 을 구성 해 야 한다. 구조 과정 에서 brown 기 존의 데이터 구 조 를 공유 하고 변 경 된 잎 노드 만 새로 추 가 했 을 뿐이다.불필요 한 저장 공간의 낭 비 를 줄 였 다. 이런 이상 적 인 저장 방식 은 가 변 적 이지 않 은 데이터 구조의 삭제 작업 에 매우 유리 하 다. 시간 복잡 도 는 O (log 2 n) 이다. 실제 저장 에서 clojure 는 2 개의 키 노드 가 아 닌 32 개의 키 노드 의 저장 방식 을 취하 고 해당 하 는 시간 복잡 도 는 O (log 32 n) 이다.n 이 매우 큰 상황 에서 O (log 32 n) 와 O (log n) 의 복잡 도 는 같다 는 것 을 알 고 있 습 니 다. 그러나 상대 적 으로 작은 데이터 에 있어 O (log 32 n) 는 O (1) 와 비슷 할 수 있 습 니 다. 이것 은 clojure 가 왜 자신 이 vector 에 대한 첨삭 작업 이 상수 시간 에 가깝다 고 말 하 는 이유 입 니 다.
2. 왜 Transient Data Structures 가 있어 야 합 니까?
* 8195: 8195: 비록 vector 데이터 구조의 저장 방식 효율 이 높 지만 저장 공간 을 빈번하게 분배 하고 저장 공간 을 쓰레기 로 회수 해 야 합 니 다. 예 를 들 어 우 리 는 다음 과 같은 작업 을 수행 해 야 합 니 다.
;; 0 9 vector
(reduce conj [] (range 10))
;;=> [0 1 2 3 4 5 6 7 8 9]
이 10 개의 수 를 배열 에 순서대로 넣 을 때마다 하나의 수 를 넣 을 때마다 헤드 포인터 가 있 는 새로운 vector 를 생산 합 니 다. 또한 앞의 vector 는 더 이상 사용 되 지 않 기 때문에 시스템 은 그 공간 에 대해 쓰레기 수 거 를 해 야 합 니 다. 앞 뒤 데이터 구조 에 저 장 된 공간 이 어느 정도 공유 되 지만 이런 조작 은 일정한 시간 을 낭비 할 수 있 습 니 다.효율 에 대한 요구 가 비교적 높 은 코드 는 받 아들 이기 어렵다.이 때문에 효율 을 높이 기 위해 클 로 저 는 Transient 데이터 형식 을 추 가 했 습 니 다. transient 는 클 로 저 데이터 구 조 를 바 꿀 수 있 습 니 다. transient 는 vector 뿐만 아니 라 set 와 map 에서 도 사용 할 수 있 지만 list 에 사용 할 수 없습니다.다음은 하나의 vector 의 요소 작업 을 업데이트 하여 transient 와 persistent 데이터 형식의 차 이 를 비교 하고 [1, 2, 3, 4, 5, 6] 을 [1, 2 F 4, 5, 6] 로 업데이트 합 니 다. 두 가지 서로 다른 데이터 유형 간 의 변화 과정 은 다음 과 같 습 니 다.
* 8195: persistent 업데이트 작업 후 두 개의 head 지침 이 있 고 두 개의 서로 다른 vector 가 있 습 니 다. transient 업데이트 작업 을 한 후에 기 존의 데이터 구 조 를 바탕 으로 한 개의 잎 노드 를 바 꾸 었 습 니 다. head 지침 이 변 하지 않 고 기 존의 vector 에 저 장 된 내용 이 바 뀌 었 기 때문에 transient 는 어느 정도 에 저장 공간의 낭 비 를 줄 였 습 니 다.코드 실행 효율 을 높 였 다.
3. Transient Data Structures 의 관련 조작 함수:
읽 기 전용 조작 에 대해 데이터 내용 을 바 꾸 지 않 기 때문에 transient data 와 persistent data 는 읽 기 전용 조작 함수 한 세트 를 공유 합 니 다. 예 를 들 어 nth, get, count 등 함수 이지 만 데 이 터 를 변경 하 는 함수 에 대해 다른 조작 함수 가 있 습 니 다. 다음은 transient data structures 데이터 구조 와 관련 된 조작 함수 에 대한 상세 한 설명 입 니 다.
(def a [1 2 3])
;;=> #'insight.main/a
;; transient transient
(def a' (transient a))
;;=> #'insight.main/a'
;;
(nth a' 2)
;;=> 3
;;
(conj! a' 4)
;; persistent!
(persistent! a')
;=> [1 2 3 4]
;; a' ,
(nth a' 2)
IllegalAccessError Transient used after persistent! call clojure.lang.PersistentVector$TransientVector.ensureEditable (PersistentVector.java:548)
(conj! a' 4)
IllegalAccessError Transient used after persistent! call clojure.lang.PersistentVector$TransientVector.ensureEditable (PersistentVector.java:548)
;; 0 9 transient vector , , vector,
(loop [i 0 v (transient [])]
(if ( [0 1 2 3 4 5 6 7 8 9]
;; 8 t key-value ,
(let [t (transient {})]
(dotimes [i 8]
(assoc! t i i))
(persistent! t))
;;=> {0 0, 1 1, 2 2, 3 3, 4 4, 5 5, 6 6, 7 7}
;; 9 t key-value , , 9 , map , t
(let [t (transient {})]
(dotimes [i 9]
(assoc! t i i))
(persistent! t))
;;=> {0 0, 1 1, 2 2, 3 3, 4 4, 5 5, 6 6, 7 7}
정확 한 사용 추가 방식 은 다음 과 같 아야 합 니 다.
;; reduce , assoc! ,
(persistent!
(reduce (fn [t i] (assoc! t i i))
(transient {})
(range 10)))
;;=>{0 0, 7 7, 1 1, 4 4, 6 6, 3 3, 2 2, 9 9, 5 5, 8 8}
4. clojure. cor e 라 이브 러 리 에서 transient data 관련 함수 와 효율 을 사용 합 니 다.
는 어떤 상황 에서 Transient Data Structurese 를 적용 합 니까?우선, 우 리 는 변 경 된 데이터 에 만 신경 을 쓰 고 원시 데 이 터 는 우리 에 게 중요 하지 않 으 며 더 이상 사용 되 지 않 을 것 이다.그 다음으로 Transient Data Structures 는 단일 스 레 드 프로그램 에 적용 된다. 모든 스 레 드 가 같은 데 이 터 를 공유 할 때 동시에 변경 하면 병발 문 제 를 일 으 킬 수 있 기 때문이다. 이것 은 clojure 가 persistent 데이터 구 조 를 사용 하 는 이유 중 하나 이다.마지막 으로 과도 데이터 구 조 는 주로 코드 효율 을 높이 기 위해 설계 되 었 기 때문에 여러 번 연속 으로 요 소 를 추가 하 는 데 transient 데이터 형식 을 사용 하 는 것 을 고려 할 수 있다. clojure. cor e 라 이브 러 리 의 관련 함수 정의 원본 코드 에 대한 검색 을 통 해 transient data structure 관련 함 수 를 찾 아 냈 습 니 다. set 함수 / into 함수 / mapv 함수 / filterv 함수 / group - by 함수 / freequencies 함수 입 니 다. 우 리 는 이러한 함수 들 의 특징 이 모두 한 서열 에 대해 변경 작업 을 하 는 것 임 을 알 수 있 지만 원시 데이터 의 내용 에 관심 이 없습니다.다음은 criterium. cor e 라 이브 러 리 의 quick - bench 함수 로 코드 운행 시간 을 테스트 하여 transient data 의 효율 을 증명 합 니 다.
;;into :
;; concat vector list, vector, :4.354145 µs
(quick-bench (vec (concat [1 2 3] (range 100 200))))
;;Evaluation count : 154098 in 6 samples of 25683 calls.
;;Execution time mean : 4.354145 µs
;; into list vector , 1.549213 µs
(quick-bench (into [1 2 3] (range 100 200)))
;;Evaluation count : 382944 in 6 samples of 63824 calls.
;;Execution time mean : 1.549213 µs
;;mapv :
;; mapv' mapv
(defn mapv' [f coll]
(loop [result [] r coll]
(if (nil? (seq r))
result
(recur (conj result (f (first r))) (rest r))
)))
;; , 794.484682 µs
(quick-bench (mapv' inc (range 10000)))
;;Evaluation count : 780 in 6 samples of 130 calls.
;;Execution time mean : 794.484682 µs
;; mapv , 197.386935 µs
(quick-bench (mapv inc (range 10000)))
;;Evaluation count : 3090 in 6 samples of 515 calls.
;;Execution time mean : 197.386935 µs
;; map vec , 418.949804 µs
(quick-bench (vec (map inc (range 10000))))
;;Evaluation count : 1482 in 6 samples of 247 calls.
;;Execution time mean : 418.949804 µs
상기 함수 에 대한 대 비 를 통 해 우 리 는 transient 함수 가 빅 데 이 터 를 조작 하 는 상황 에서 확실히 우리 에 게 많은 시간 을 절약 할 수 있다 는 것 을 알 게 되 었 다. 그래서 평소에 코드 를 쓸 때 반드시 좋 은 습관 을 길러 야 한다. 코드 효율 을 높이 기 위해 상기 에서 언급 한 편 지 를 되도록 사용 해 야 한다.
5. 요약:
오늘 은 주로 방금 배 운 transient data structures 를 귀납 적 으로 정리 했다. 과도 데이터 구 조 는 코드 효율 의 향상 에 큰 역할 을 한다. 이 데이터 유형 은 map, vector, map 에 응용 할 수 있다. 만약 에 우리 가 원시 데이터 에 관심 이 없다 면 변 경 된 데이터, 특히 여러 번 이런 조작 을 할 수 있다.그러면 우 리 는 과도 데이터 구 조 를 사용 하 는 것 을 고려 할 수 있 습 니 다. clojure. core 에서 일부 함 수 는 과도 데이터 구 조 를 사 용 했 기 때문에 우 리 는 가능 한 한 인 코딩 할 때 이런 함 수 를 사용 하여 코드 효율 을 높 일 수 있 습 니 다.
클 라 우 드 안전 (방패) 콘 텐 츠 안전, 인증 코드 등 서비스 무료 체험
더 많은 왕 이 기술, 제품, 운영 경험 공유 클릭 하 세 요.
추천 [전문가 출석] 네 가지 병행 프로 그래 밍 모델 소개 [추천] 브 라 우 저 에서 사용 할 NPM 패 키 지 를 만 듭 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
.net core 주입 의 세 가지 모델:Singleton,Scoped,Transient우 리 는 모두 Startup 의 Configure Services 에서 우리 가 원 하 는 서 비 스 를 주입 할 수 있다 는 것 을 알 고 있 습 니 다.그러면 주입 할 때 세 가지 모델 을 선택 할 수 있 습 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.