Programming Clojure - Unifying Data with Sequences
Collections that can be viewed as seqs are called seq-able. In this chapter, you will meet a variety of seq-able collections:
• All Clojure collections • All Java collections • Java arrays and strings • Regular expression matches • Directory structures • I/O streams • XML trees
Sequence라는 추상화는Lisp-like 언어에 매우 중요하다. 왜냐하면 이런 FP 언어에서 거의 모든 것이list이고 모두 sequence이기 때문에Lisp, 바로listprocess 언어이다.위에서 여러 가지 추상적인 구체적인 표현을 열거하였다.그래서 sequences의 조작이 굉장히 중요해요.
4.1 Everything Is a Sequence
Every aggregate data structure in Clojure can be viewed as a sequence. A sequence has three core capabilities:
(first aseq) ;get the first item
(rest aseq) ;get everything after the first item
(cons elem aseq) ;construct a new sequence by adding an item
sequence는 사실 3개의 핵심 조작인 퍼스트,rest,cons만 있고 매우 간단하다. 다른 각종 조작은 모두 이 3개의 조작을 바탕으로 이루어진 것이다.
왜 퍼스트,rest만 제공합니까?이상하게 생각하기 시작했는데 개인적으로 이 두 가지 조작은 이미 충분하다고 생각한다. 만약에 반복하려고 하면rest에first를 계속 가져간다. 만약에 몇 개의 item을 찾으려면first(rest)를 몇 번 교체해야 한다. 게다가 lazy sequence의 디자인,rest는 모두 먼저 계산하지 않고 first가 도착하면 다시 계산할 수 있다.
(first '(1 2 3))
1
(rest '(1 2 3))
(2 3)
(cons 0 '(1 2 3))
(0 1 2 3)
; seq-able seq , seq. vector seq
; clojure , REPL seq , seq
(rest [1 2 3])
(2 3)
(first {:fname "Stu" :lname "Halloway"})
[:fname "Stu"]
(first #{:the :quick :brown :fox}) ;set , first
:brown
(sorted-set :the :quick :brown :fox) ; set
#{:brown :fox :quick :the}
(sorted-map :c 3 :b 2 :a 1) ; map
{:a 1, :b 2, :c 3}
conj adds one or more elements to a collection, and into adds all the items in one collection to another.
(conj '(1 2 3) :a)
(:a 1 2 3)
(into '(1 2 3) '(:a :b :c))
(:c :b :a 1 2 3)
4.2 Using the Sequence Library
The Clojure sequence library provides a rich set of functionality that can work with any sequence.
The functions provide a rich backbone of functionality that can take advantage of any data structure that obeys the basic first/rest/cons contract.
The following functions are grouped into four broad categories: • Functions that create sequences • Functions that filter sequences • Sequence predicates • Functions that transform sequences
Creating Sequences
range
(range start? end step?)
(range 10)
(0 1 2 3 4 5 6 7 8 9)
(range 10 20)
(10 11 12 13 14 15 16
17 18 19)
(range 1 25 2)
(1 3 5 7 9 11 13 15 17 19 21 23)
repeat
(repeat n x)
(repeat 5 1)
(1 1 1 1 1)
(repeat 10 "x")
("x" "x" "x" "x" "x" "x" "x" "x" "x" "x")
iterate , infinite extension of range
(iterate f x)
(take 10 (iterate inc 1)) ;take infinite, seq seq
(1 2 3 4 5 6 7 8 9 10)
cycle
(cycle coll)
(take 10 (cycle (range 3)))
(0 1 2 0 1 2 0 1 2 0)
interleave
takes multiple collections and produces a new collection that interleaves values from each collection until one of the collections is exhausted
(interleave & colls) ;가변 길이
(interleave (whole-numbers) ["A" "B" "C" "D" "E"])
(1 "A" 2 "B" 3 "C" 4 "D" 5 "E")
interpose each of the elements of the input collection separated by a separator
(interpose separator coll)
(interpose "," ["apples" "bananas" "grapes"])
("apples" "," "bananas" "," "grapes")
(apply str (interpose \, ["apples" "bananas" "grapes"])) ; string
"apples,bananas,grapes"
(use '[clojure.contrib.str-utils :only (str-join)])
(str-join \, ["apples" "bananas" "grapes"]) ;
"apples,bananas,grapes"
Filtering Sequences
filter
(filter pred coll)
(take 10 (filter even? (whole-numbers)))
(2 4 6 8 10 12 14 16 18 20)
(take 10 (filter odd? (whole-numbers)))
(1 3 5 7 9 11 13 15 17 19)
take-while, drop-while (take-while pred coll) = while (pred?) {take element from coll}, 따라서 첫 번째 조건에 부합되지 않는 사람을 만나면 take를 중지합니다
(drop-while pred coll)
(take-while even? [2 4 6 1 8]) ; [2 4 6]
(take-while #(> % 0) [3 2 1 0 -1 -2]) ; (3 2 1)
(drop-while even? [2 4 6 1 3 5]) ; [1 3 5]
(drop-while even? [1 3 2 4 5]) ; [1 3 2 4 5] 1 drop, vector
split-at, split-with
split-at takes an index, and split-with takes a predicate
(split-at 5 (range 10))
[(0 1 2 3 4) (5 6 7 8 9)]
(split-with #(<= % 10) (range 0 20 2))
[(0 2 4 6 8 10) (12 14 16 18)]
Sequence Predicates
every?
(every? pred coll)
(every? odd? [1 3 5])
true
(every? odd? [1 3 5 8])
false
some
(some pred coll)
some returns the first nonfalse value for its predicate or returns nil if no element matched
(some even? [1 2 3])
true
(some even? [1 3 5])
nil
not-every, not-any
(not-every? even? (whole-numbers))
true
(not-any? even? (whole-numbers))
false
Transforming Sequences
map
(map f coll)
(map #(format "<p>%s</p>" %) ["the" "quick" "brown" "fox"])
("<p>the</p>" "<p>quick</p>" "<p>brown</p>" "<p>fox</p>")
(map #(format "<%s>%s</%s>" %1 %2 %1) ["h1" "h2" "h3" "h1"] ["the" "quick" "brown" "fox"])
("<h1>the</h1>" "<h2>quick</h2>" "<h3>brown</h3>" "<h1>fox</h1>")
reduce
(reduce f coll)
reduce applies f on the first two elements in coll, then applies f to the result and the third element, and so on. reduce is useful for functions that “total up” a sequence in some way.
(reduce + (range 1 11))
55
sort, sort-by
(sortcomp?coll)(sort-by a-fn comp?coll)는sort보다'a-fn'이 많고,coll에 대해서는 apply a-fn, 그리고sort
(sort [42 1 7 11])
(1 7 11 42)
(sort > [42 1 7 11])
(42 11 7 1)
(sort-by #(.toString %) [42 1 7 11]) ; str , 7>42
(1 11 42 7)
(sort-by :grade > [{:grade 83} {:grade 90} {:grade 77}])
({:grade 90} {:grade 83} {:grade 77})
List Comprehension
The granddaddy of all filters and transformations is the list comprehension. A list comprehension creates a list based on an existing list, using set notation.
이것은python에서도 매우 중요한 특성이다.
(for [binding-form coll-expr filter-expr? ...] expr) for takes a vector of binding-form/coll-exprs, plus an optional filter-expr, and then yields a sequence of exprs. List comprehension is more general than functions such as map and filter and can in fact emulate most of the filtering and transformation functions described earlier.
(for [word ["the" "quick" "brown" "fox"]] (format "<p>%s</p>" word))
("<p>the</p>" "<p>quick</p>" "<p>brown</p>" "<p>fox</p>")
Comprehensions can emulate filter using a :when clause
(take 10 (for [n (whole-numbers) :when (even? n)] n))
(2 4 6 8 10 12 14 16 18 20)
A:while clause continues the evaluation only while its expression holds true: when과 달리 맞지 않으면 stop
(for [n (whole-numbers) :while (even? n)] n)
(0)
The real power of for comes when you work with more than one binding expression.
(for [file "ABCDEFGH" rank (range 1 9)] (format "%c%d" file rank))
("A1" "A2" ... elided ... "H7 ""H8")
;Clojure iterates over the rightmost binding expression in a sequence comprehension first and then works its way left
(for [rank (range 1 9) file "ABCDEFGH"] (format "%c%d" file rank))
("A1" "B1" ... elided ... "G8" "H8") ;
4.3 Lazy and Infinite Sequences
Most Clojure sequences are lazy; in other words, elements are not calculated until they are needed. Using lazy sequences has many benefits:
• You can postpone expensive computations that may not in fact be needed. • You can work with huge data sets that do not fit into memory. • You can delay I/O until it is absolutely needed.
Forcing Sequences
어떤 때는 레이지가 싫기 때문에force seq가 필요하지만 되도록 쓰지 마세요.
The problem usually arises when the code generating the sequence has side effects. Consider the following sequence, which embeds side effects via println:
(def x (for [i (range 1 3)] (do (println i) i))) ; lazy, print
#'user/x
doall, dorun
(doall coll)
doall forces Clojure to walk the elements of a sequence and returns the elements as a result:
(doall x)
| 1
| 2
) (1 2)
(dorun coll) 결과를 유지하지 않고 Dorun walks the elements of a sequence without keeping past elements in memory를 반복합니다.As a result, dorun can walk collections too large to fit in memory.
(dorun x)
| 1
| 2
nil
The nil return value is a telltale reminder that dorun does not hold a reference to the entire sequence.
The dorun and doall functions help you deal with side effects, while most of the rest of Clojure discourages side effects. You should use these functions rarely. (The Clojure core calls each of these functions only once in about 4,000 lines of code.)
4.4 Clojure Makes Java Seq-able
The seq abstraction of first/rest applies to anything that there can be more than one of.
In the Java world, that includes the following: • The Collections API • Regular expressions • File system traversal • XML processing • Relational database results Clojure wraps these Java APIs, making the sequence library available for almost everything you do.
건너뛰다
4.5 Calling Structure-Specific Functions
Clojure’s sequence functions allow you to write very general code. Sometimes you will want to be more specific and take advantage of the characteristics of a specific data structure. Clojure includes functions that specifically target lists, vectors, maps, structs, and sets.
우선 편의를 위해 더욱 중요한 것은 효율이다. 통용되는 방법은 더욱 추상적이지만 비교적 효과가 없다.
Functions on Lists
(peek '(1 2 3)) ; first
1
(pop '(1 2 3)) ; rest
(2 3)
Functions on Vectors
get
(get [:a :b :c] 1)
:b
(get [:a :b :c] 5)
nil
([:a :b :c] 1) ;vector function
:b
([:a :b :c] 5)
java.lang.ArrayIndexOutOfBoundsException: 5 ; get ,
assoc associates a new value with a particular index:
(assoc [0 1 2 3 4] 2 :two)
[0 1 :two 3 4]
subvec returns a subvector of a vector: (subvec avec start end?)
(subvec [1 2 3 4 5] 3) ;end is not specified, it defaults to the end of the vector
[4 5]
(subvec [1 2 3 4 5] 1 3)
[2 3]
Functions on Maps
(keys {:sundance "spaniel", :darwin "beagle"})
(:sundance :darwin)
(vals {:sundance "spaniel", :darwin "beagle"})
("spaniel" "beagle")
(get {:sundance "spaniel", :darwin "beagle"} :darwin)
"beagle"
(get {:sundance "spaniel", :darwin "beagle"} :snoopy)
nil
({:sundance "spaniel", :darwin "beagle"} :darwin) ;map function
"beagle"
({:sundance "spaniel", :darwin "beagle"} :snoopy)
nil
(:darwin {:sundance "spaniel", :darwin "beagle"} ) ;keyword function
"beagle"
(:snoopy {:sundance "spaniel", :darwin "beagle"} )
nil
assoc returns a map with a key/value pair added. dissoc returns a map with a key removed. select-keys returns a map, keeping only the keys passed in merge combines maps. If multiple maps contain a key, the rightmost map wins.
(def song {:name "Agnus Dei" :artist "Krzysztof Penderecki"
:album "Polish Requiem"
:genre "Classical" })
(assoc song :kind "MPEG Audio File") ;add kind:"MPEG Audio File"
{:name "Agnus Dei", :album "Polish Requiem",
:kind "MPEG Audio File", :genre "Classical",
:artist "Krzysztof Penderecki"}
(dissoc song :genre) ; rmove genre
{:name "Agnus Dei", :album "Polish Requiem",
:artist "Krzysztof Penderecki"}
(select-keys song [:name :artist])
{:name "Agnus Dei", :artist "Krzysztof Penderecki"}
(merge song {:size 8118166, :time 507245})
{:name "Agnus Dei", :album "Polish Requiem",
:genre "Classical", :size 8118166,
:artist "Krzysztof Penderecki", :time 507245}
merge-with is like merge, except that when two or more maps have the same key, you can specify your own function for combining the values under the key.
(merge-with merge-fn &maps) 같은 키가 나타날 때의 논리를 사용자 정의할 수 있습니다
(merge-with
concat ;merge-fn
{:rubble ["Barney"], :flintstone ["Fred"]}
{:rubble ["Betty"], :flintstone ["Wilma"]}
{:rubble ["Bam-Bam"], :flintstone ["Pebbles"]})
{:rubble ("Barney" "Betty" "Bam-Bam"),
:flintstone ("Fred" "Wilma" "Pebbles")}
Functions on Sets
union, intersection, difference, select
(def languages #{"java" "c" "d" "clojure" })
(def letters #{"a" "b" "c" "d" "e" })
(def beverages #{"java" "chai" "pop" })
;union returns the set of all elements present in either input set,
(union languages beverages)
#{"java" "c" "d" "clojure" "chai" "pop"}
;intersection returns the set of all elements present in both input sets,
(intersection languages beverages)
#{"java"}
;difference returns the set of all elements present in the first input set, minus those in the second
(difference languages beverages) ;languages beverages , b , l
#{"c" "d" "clojure"}
;select returns the set of all elements matching a predicate
(select #(= 1 (.length %)) languages)
#{"c" "d"}
Relational Algebra
Set union and difference are part of set theory, but they are also part of relational algebra, which is the basis for query languages such as SQL. The relational algebra consists of six primitive operators: set union and set difference (described earlier), plus rename, selection, projection, and cross product.
이것은 매우 편리하고 재미있으며 관계 대수를 지지한다.줄마다 맵으로 표시하고, 표는 맵의 set으로 표시합니다.
(def compositions
#{{:name "The Art of the Fugue" :composer "J. S. Bach" }
{:name "Musical Offering" :composer "J. S. Bach" }
{:name "Requiem" :composer "Giuseppe Verdi" }
{:name "Requiem" :composer "W. A. Mozart" }})
(def composers
#{{:composer "J. S. Bach" :country "Germany" }
{:composer "W. A. Mozart" :country "Austria" }
{:composer "Giuseppe Verdi" :country "Italy" }})
(def nations
#{{:nation "Germany" :language "German" }
{:nation "Austria" :language "German" }
{:nation "Italy" :language "Italian" }})
The rename function renames keys (“database columns”), based on a map from original names to new names. (rename relation rename-map)
(rename compositions {:name :title}) ; name title
#{{:title "Requiem", :composer "Giuseppe Verdi"}
{:title "Musical Offering", :composer "J.S.Bach"}
{:title "Requiem", :composer "W. A. Mozart"}
{:title "The Art of the Fugue", :composer "J.S. Bach"}}
The select function returns maps for which a predicate is true and is analogous to the WHERE portion of a SQL SELECT: (select pred relation)
(select #(= (:name %) "Requiem") compositions) ;name Requiem
#{{:name "Requiem", :composer "W. A. Mozart"}
{:name "Requiem", :composer "Giuseppe Verdi"}}
The project function returns only the portions of the maps that match a set of keys. (project relation keys)
(project compositions [:name])
#{{:name "Musical Offering"}
{:name "Requiem"}
{:name "The Art of the Fugue"}}
The cross product returns every possible combination of rows in the different tables. You can do this easily enough in Clojure with a list comprehension: (for [m compositions c composers] (concat m c)) ;전체 조합4 x 3 = 12 rows ...
Although the cross product is theoretically interesting, you will typically want some subset of the full cross product. For example, you might want to join sets based on shared keys: (join relation-1 relation-2 keymap?)
(join compositions composers) ;join the composition names and composers on the shared key :composer
(join composers nations {:country :nation}) ; country nation map
You can combine the relational primitives.
(project
(join
(select #(= (:name %) "Requiem") compositions)
composers)
[:country])
#{{:country "Italy"} {:country "Austria"}}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
표준 Pascal 범위 내에서 Delphi 입문표준 Pascal (Standard Pascal) 에서 해설되고 있는 범위에서의 Delphi 는 어떻게 되어 있는지를, 문득 알고 싶어졌습니다. 이 기사는 ** "Delphi 콘솔 응용 프로그램에서 uses 절을 작...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.