Datový typ map!

1. Úvod

Struktura typu map! reprezentuje asociativní array dvojic key/value. Poskytuje rychlý přístup pro čtení s interním použitím hashtable. Na rozdíl od datového typu hash! nepatří map! do skupiny series!, tudíž nezná koncept pozic (nemá interní iterátor). Koncepčně leží typ map! mezi datovými typy hash! a object!. Uživatelsky připomíná slovník.

2. Literálová skladba

#(<key> <value>...)

<key> : hashovaný klíč, akceptované typy jsou: scalar!, all-word!, any-string! <value> : hodnota typu any-type!

#(a: 1 b: 2)
== #(
    a: 1
    b: 2
)

3. Konstruktorová (creation) skladba

make map! <spec>

<spec> : blok párů key/value nebo jedno celé číslo.

make map! [a 1 'b 2 "c" 3 4 5]
== #(
    a: 1
    b: 2
    "c" 3
	4 5      )

>> mp/4              ; == 5

Je-li argumentem funkce celé číslo, vytvoří se prázdný objekt map! se zadaným počtem slotů (obvykle za účelem pozdějšího dynamického zaplnění mapy).

make map! 10
== #()

Tělo mapy nebo specifikačního bloku musí obsahovat sudý počet elementů, jinak je generována chyba. Hodnoty nejsou redukovány, takže pro určité hodnoty (např. true, false, none) je vyžadována strukturální (construction) forma deklarace.

Příklad:

>> mop: #(a b c: #[true] d: [true] e true)
== #(
    a: b
    c: true
    d: [true]
    e: true 	)

>> mop/c == mop/e	       ; == false

>> type? mop/a             ; == word!
>> type? mop/c             ; == logic!
>> type? mop/d             ; == block!
>> type? mop/e             ; == word!

Je-li klíč typu any-word (zde a, e), je v mapě kvůli lepší přehlednosti konvertován na typ set-word. Nicméně, při dotazování na hodnotu klíčů, lze pro klíč z praktických důvodů (jednodušší zápis, zejména u cest) použít jednoduchý tvar slova místo typu set-word!.

Strukturální skladba má pro některá slova formát:

#[...]

Tato konstrukce je převzata z Rebou a v Redu se používá pro slova typu logic! a none!

Note
  • Typ map! je implicitně case-preserving (case-insensitive) při vyhledávání stejně jako hash! a block!.

  • Je-li jako hodnota páru zadána hodnota none, klíč se nevytvoří (viz sekci "Mazání klíčů").

  • Při vytváření mapy jsou všechny klíče "deep copied".

  • Hodnoty typu series! nejsou při vytváření mapy kopírovány; volba je na uživateli (?) - což optimalizuje zdroje pro obecné použití.

Jiný způsob vytvoření nové mapy je s použitím akce copy u mapy existující.

4. Vyjímání hodnot

Za účelem demonstrací si definujme mapu m:

m: #(Ab: 2 aB: 5 ab: 10)

Vyjímání s použitím cesty:

<map>/<key>

get '<map>/<key>

<map>: slovo, odkazující na kolektor typu map!
<key>: slovo, označující klíč

m/ab             ; == 2
get 'm/ab        ; == 2

S použitím výběrové funkce select:

select <map> <'key>

select m 'ab     ; == 2

Pokus o vyhledání v mapě nedefinovaného klíče vrací hodnotu none.

5. Změna klíčů a hodnot

S použitím cesty:

<map>/<key>: <value>
set '<map>/<key> <value>

<map>   : word referring to a map! value
<key>   : word key to select a value in the map
<value> : any value

Vložení dvojice:

put <map> '<key> <value>

<map> : map value
<key> : any valid key value to select a value in the map

Hromadné změny:

extend <map> <spec>

<map>  : a map value
<spec> : block of name/value pairs (one or more pairs)

Všechny tyto zápisy jsou case-insensitive. Pro case-sensitive vzhled je potřebné použít zjemnění /case, je-li to možné:

set/case '<map>/<key> <value>
put/case <map> <key> <value>
extend/case <map> <spec>

get 'm/ab          ; == 2

get/case 'm/ab     ; == 10
select/case m 'aB  ; == 5

Nativní funkce extend může přijmout více klíčů najednou, takže je vhodná pro hromadné změny:

>> m: make map! 5             ; == #()

>> extend m [a: 5 b: none!]
== #(
    a: 5             ; type integer!
    b: none!         ; type word!
)



[NOTE]
====

* zadání klíče, který dosud v mapě neexistuje, způsobí jeho vytvoření.
* přidání existujícího klíče změní jeho hodnotu, přičemž se implicitně provádí *case-insensitive* porovnávání.
* přidání hodnoty `none` typu word! způsobí smazání dvojice klíč: value - viz Mazání klíčů.

====

Příklady:

```red
m: #(Ab: 2 aB: 5 ab: 10)

m/ab: 3               ; == 3

put m 'Ab "hello"     ; == "hello"

m                     ; == #(Ab: "hello aB: 5 ab: 10)

set/case 'm/aB 0

set/case 'm/ab 192.168.0.1

m                     ; == #(Ab: "hello aB: 0 ab: 192.168.0.1)


n: #(%cities.red 10)
extend n [%cities.red 99 %countries.red 7 %states.red 27]
m
== #(
	%cities.red 99
	%countries.red 7
	%states.red 27
)

6. Mazání klíčů

Dvojici key/value jednoduše z mapy vymažeme přiřazením hodnoty none (typu none!) ke stávajícímu klíči - s použitím jednoho z možných způsobů:

m: #(a: 1 b 2 "c" 3 d: 99)
== #(
    a: 1
    b: 2
    "c" 3
    d: 99
)

>> m/b: none                ; == none

>> put m "c" none           ; == none

>> extend m [d #[none]]     ; == #(a: 1)

; nebo: extend m #(d: none)

>> m                        ; == #(a: 1)

Je rovněž možné smazat všechny klíče najednou funkcí clear:

clear m                     ; == #()
Note
  • Z definice funkce extend plyne, že připojovaná hodnota má být typu block!, map! nebo hash!.

  • Všimněte si rozdílné formy pro vyjádření hodnoty none typu none! v připojovaném bloku a mapě.

  • V připojovaném bloku byla pro přiřazení použita strukturální (construction) skladba.

7. Reflexe

Pro práci s mapou (slovníkem) se s výhodou použijí další pomocné funkce:

  • find ověří přítomnost klíče v mapě a vrátí true, byl-li nalezen, v opačném případě vrátí none.

    find #(a 123 b 456) 'b
    == true
  • length? vrací počet dvojic key/value v mapě.

    length? #(a 123 b 456)
    == 2
  • keys-of vrací seznam klíčů v mapě formou bloku (set-words are converted to words).

    keys-of #(a: 123 b: 456)
    == [a b]
  • values-of vrací seznam hodnot v mapě.

    values-of #(a: 123 b: 456)
    == [123 456]
  • body-of vrací všechny dvojice key/value v mapě.

    body-of #(a: 123 b: 456)
    == [a: 123 b: 456]

results matching ""

    No results matching ""