Skip to content

Instantly share code, notes, and snippets.

@guruma
Created March 11, 2019 04:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guruma/f3ad56c5eba77943f1f8099a19bbd02d to your computer and use it in GitHub Desktop.
Save guruma/f3ad56c5eba77943f1f8099a19bbd02d to your computer and use it in GitHub Desktop.
(ns ch3.core)
;;;;;;;;;;;;;;
;; Chapter 3
;;;;;;;;;;;;;;
;; 리스트는 앞에 추가한다.
(def lst '(1 2 3))
(conj lst 0)
(conj lst 0 -1) ; 여러개 추가 가능
(seq lst)
;; 벡터는 뒤에 추가한다.
(def v [1 2 3])
(conj v 4)
(conj v 4 5)
(seq v)
(def m {:a 5 :b 6})
(conj m [:c 7])
(conj m [:c 7] [:d 8])
(seq m)
(def s #{1 2 3})
(conj s 4)
(conj s 3 4)
(seq s)
;; conj nil
(conj nil 3 4)
;; into 리스트는 앞에 추가
(into '(1 2 3) [:a :b :c])
(defn swap-pairs [sequential]
(into (empty sequential)
(interleave
(take-nth 2 (drop 1 sequential))
(take-nth 2 sequential))))
(empty [1 2])
(swap-pairs (apply list (range 10)))
(swap-pairs (apply vector (range 10)))
(defn map-map [f m]
(into (empty m)
(for [[k v] m]
[k (f v)])))
(map-map inc (hash-map :z 5 :c 6 :a 0))
(map-map inc (sorted-map :z 5 :c 6 :a 0))
;; Sequence 추상
;;
;; seq 함수 : 각 타입의 시퀀스 뷰 제공
;; 인터페이스 함수 : first, rest, next
;; clojure.lang.ISeq
;;
;; 시퀀스는 리스트가 아니다.
;; 리스트는 eliment 모은 collection이지만
;; 시퀀스는 head와 tail로 된 pair(tuple)이다. 즉 시퀀스에는 eliment라는 개념이 없다.
;; https://books.google.co.kr/books?id=4QacPa1vwMUC&pg=PA76&lpg=PA76&dq=practical+clojure+sequence+illustration&source=bl&ots=2AECdpi3oc&sig=ACfU3U2UabuOn0Zv04bArk-xo8DSE-gFfw&hl=ko&sa=X&ved=2ahUKEwjQnM3UpfTgAhUowosBHcKjB1YQ6AEwAHoECAIQAQ#v=onepage&q=practical%20clojure%20sequence%20illustration&f=false
;; sequable types
;;
;; All Clojure collection types
;; All Java collections (i.e., java.util.*)
;; All Java maps
;; All java.lang.CharSequences, including Strings
;; Any type that implements java.lang.Iterable
;; Arrays
;; nil (i.e., null as returned from Java methods)
;; Anything that implements Clojure’s clojure.lang.Seqable interface
(seq "Clojure")
(seq {:a 5 :b 6})
(seq (java.util.ArrayList. (range 5)))
(seq (into-array ["Clojure" "Programming"]))
(if (seq []))
(seq nil)
(not (empty? nil))
;; 대부분의 함수들이 내부적으로 인자에 seq를 호출해서 사용하기 때문에, seq를 해서 넘길 필요가 없다.
(map str "Clojure")
(set "Programming")
;; clojure.lang.ISeq
;; https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/ISeq.java
;; https://clojure.org/reference/sequences
(map seq? ['() [] #{} {}])
(map sequential? ['() [] #{} {}])
(map coll? ['() [] #{} {}])
;(map seqable? ['() [] #{} {}]) ; clojure 1.9
;= [true true true true]
(clojure-version)
(rest [1])
(next [1])
(rest [])
(next [])
(rest nil)
(next nil)
(def x '())
(= (next x)
(seq (rest x)))
(type (range 10))
(list? (range 10))
;; 시퀀스는 이터레이터가 아니다.
(doseq [x (range 3)]
(println x))
(let [r (range 3)
rst (rest r)]
(prn (map str rst))
(prn (map #(+ 100 %) r))
(prn (conj r -1) (conj rst 42)))
;; 시퀀스는 리스트가 아니다.
;; 리스트는 길이 정보를 관리한다. 그래서 length를 하면 바로 길이를 리턴한다.
(let [s (range 1e6)]
(time (count s)))
(let [s (apply list (range 1e6))]
(time (count s)))
;; 시퀀스 만들기
(type (cons 0 (range 1 5)))
(cons :a [:b :c :d])
(cons 0 (cons 1 (cons 2 (cons 3 (range 4 10)))))
(type (list* 0 1 2 3 (range 4 10)))
(type (list 1 2 3))
;; lazy-seq
;; clojure 1.2에서는 lazy-cons를 사용했는데 없어지고 lazy-seq를 사용한다.
;; collection define X, access O
(type (lazy-seq [1 2 3]))
(defn random-ints
"Returns a lazy seq of random integers in the range [0,limit)."
[limit]
(lazy-seq
(cons (rand-int limit)
(random-ints limit))))
(take 10 (random-ints 50))
;; (1.f)
;; (1.(2.f))
;; lazy-seq, realized
(defn random-ints [limit]
(lazy-seq
(log "realizing random number") ; side effect, 관문
(cons (rand-int limit)
(random-ints limit))))
(def rands (take 10 (random-ints 50)))
(first rands)
(nth rands 3)
(count rands)
(count rands)
;; lazy-seq = value -> thunk
(repeatedly 10 (partial rand-int 50))
(def x (next (random-ints 50))) ; head rest
(def x (rest (random-ints 50))) ; head
(doall (take 5 (random-ints 50))) ; retention
(dorun (take 5 (random-ints 50))) ; dumped out
;; doc을 보면 lazy-seq인지 여부를 알 수 있다.
iterate
reverse
;; lazy-seq는 define할 때가 아니라, access할 때 realized 되기 때문에
;; side-effect가 뒤늦게 발생할 수 있다.
;; 예를 들어 로깅의 경우, realized 될 때 로깅이 된다면, 어느 순간 로깅이 켜지거나 꺼질 때
;; 로깅이 되기도 하고 안되기도 하는 문제가 발생할 수 있다.
;; 게다가, 경우에 따라 어떤 lazy-seqs는 성능상 chunk 단위로 한꺼번에 realized 하는 경우도 있다.
;; 예들 들어 vector의 경우 32개의 요소 단위로 chunk된 시퀀스를 만든다.
;; 이런 경우, 어떤 하나의 요소에 access해도 32개의 realization이 발행할 수 있다.
;; 따라서, 클로저의 lazy-seq를 제어 흐름의 도구로 사용하면 안된다.
;; 계산 과정의 일시적 매개체로서의 시퀀스
(apply str (remove (set "aeiouy")
"vowels are useless! or maybe not..."))
(remove #{1 2 5} (range 10))
; lazy-seq retention
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; head retention
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(split-with neg? (range -5 5))
;= [(-5 -4 -3 -2 -1) (0 1 2 3 4)]
;(let [[t d] (split-with #(< % 12) (range 1e8))]
; [(count d) (count t)])
;= #<OutOfMemoryError java.lang.OutOfMemoryError: Java heap space>
(let [[t d] (split-with #(< % 12) (range 1e8))]
[(count t) (count d)])
;= [12 99999988]
;; https://stackoverflow.com/questions/15994316/clojure-head-retention/21803773
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment