雜湊集合是可變的無序集合,每個值最多只儲存一次。查找、插入和刪除的平均時間複雜度都是 O(1)。它們位於hash-sets中,並實作了sets的協定。
HS{ "NS-1024" "WB-203" } .
! => HS{ "NS-1024" "WB-203" }
HS{ }是空字面值。就像其他 Factor 字面值一樣,它是個共享物件,原始碼中每個HS{ }的參照都指向同一個集合。HS{ } clone每次呼叫都會給你一份全新、獨立的副本。
>hash-set ( seq -- set ) ! build a fresh set from a sequence
當你已經把值放在序列裡時,>hash-set會一步把它們轉換過來,並捨棄重複的值。
USING: hash-sets prettyprint ;
{ "NS-1024" "WB-203" "NS-1024" } >hash-set .
! => HS{ "NS-1024" "WB-203" }
adjoin ( elt set -- ) ! insert in place; no-op if already present
adjoin-all ( seq set -- ) ! insert every element of seq in place
delete ( elt set -- ) ! remove in place; no-op if absent
這三個都會就地修改集合,都不會在堆疊上留下任何東西。
USING: hash-sets kernel sets ;
HS{ } clone
"NS-1024" over adjoin
"WB-203" over adjoin
"NS-1024" over adjoin ! duplicate — no effect
. ! => HS{ "NS-1024" "WB-203" }
adjoin-all是批次形式:它會加入序列中的每個元素,就像adjoin一次加入一個時那樣略過重複項。
USING: hash-sets kernel sets ;
HS{ "NS-1024" } clone
{ "WB-203" "NS-1024" "QR-7" } over adjoin-all
. ! => HS{ "QR-7" "NS-1024" "WB-203" } (order not guaranteed)
in? ( elt set -- ? ) ! is elt in the set?
null? ( set -- ? ) ! is the set empty?
cardinality ( set -- n ) ! number of elements
members ( set -- seq ) ! enumerate as a sequence
in?是集合協定的成員測試。它和member?(來自sequences)不同,後者會對序列做線性掃描。對雜湊集合來說,in?是雜湊查找,平均 O(1),而這正是使用雜湊集合的重點。
null?會回報集合是否不含任何元素,這是直接詢問「這個集合是空的嗎?」的方式,而不必把cardinality拿來和零比較。
: my-log ( -- set ) HS{ "NS-1024" "WB-203" } ;
"NS-1024" my-log in? . ! => t
"X-99" my-log in? . ! => f
my-log null? . ! => f
HS{ } clone null? . ! => t
my-log cardinality . ! => 2
union ( set1 set2 -- set )
intersect ( set1 set2 -- set )
diff ( set1 set2 -- set )
set-like ( set exemplar -- set' )
union是「來自任一邊的所有元素」;intersect是「兩邊都有的元素」;diff是「在set1裡但不在set2裡的元素」。每一個都會回傳新的集合,不會修改它的輸入。
set-like會把set強制轉型成exemplar的型別。union/intersect/diff的預設實作會用它讓結果符合接收者的型別,所以<my-set> <hash-set> union會回傳my-set,而不是雜湊集合。
雜湊集合和雜湊表在圖形走訪上是天生的搭檔:雜湊表把每個節點對應到它的相鄰節點,雜湊集合則記錄哪些節點已經走訪過,讓搜尋不會打轉或重複做白工。走訪本身則是一個佇列加上這兩個結構,在你向外掃描的過程中就地修改。
你是 Cape Crozier 燈塔的看守人。你在燈室裡記錄每一艘經過的船隻,每艘船都以專屬的呼號辨識,同時也和沿岸其他燈塔協調訊號中繼。
定義empty-log,讓它回傳一個全新的空雜湊集合,用來收集呼號。
empty-log .
! => HS{ }
定義sight,讓它接受一本日誌本和一個呼號,並就地把這筆目擊記錄下來。不回傳任何值。
empty-log dup "NS-1024" sight .
! => HS{ "NS-1024" }
定義seen?,讓它接受一本日誌本和一個呼號;如果該呼號已經記錄過就回傳t,否則回傳f。
HS{ "NS-1024" "WB-203" } "NS-1024" seen? . ! => t
HS{ "NS-1024" "WB-203" } "X-99" seen? . ! => f
定義forget-sighting,讓它接受一本日誌本和一個呼號,並就地從日誌中移除該呼號。不回傳任何值。如果呼號不在日誌裡,就什麼都不做。
HS{ "NS-1024" "WB-203" } clone dup "WB-203" forget-sighting .
! => HS{ "NS-1024" }
定義unique-count,讓它回傳日誌中不同呼號的數量。
HS{ "NS-1024" "WB-203" "AC-77" } unique-count . ! => 3
empty-log unique-count . ! => 0
海岸巡防隊維護一份relay-map:這是一張以燈塔名稱為鍵的雜湊表,每個值是一個陣列,列出該燈塔可以直接中繼到的燈塔。
定義reachable,讓它接受一個start燈塔和一份relay-map,並回傳一個雜湊集合,包含所有能從start透過反覆中繼抵達的燈塔(包括start本身)。
H{
{ "Crozier" { "Beacon" "Hadley" } }
{ "Beacon" { "Crozier" "Spiral" } }
{ "Hadley" { "Crozier" } }
{ "Spiral" { "Beacon" "Outpost" } }
{ "Outpost" { "Spiral" } }
{ "Far-Isle" { "Lonely" } }
{ "Lonely" { "Far-Isle" } }
}
"Crozier" swap reachable .
! => HS{ "Crozier" "Beacon" "Hadley" "Spiral" "Outpost" }
Far-Isle與Lonely這對燈塔自成一個連通分量,所以兩者都不會出現在結果中。