Les hash-sets sont des collections mutables et non ordonnées qui stockent chaque valeur au plus une fois. La recherche, l'insertion et la suppression sont toutes en O(1) en moyenne. Ils sont définis dans [hash-sets] et implémentent le protocole sets.
HS{ "NS-1024" "WB-203" } .
! => HS{ "NS-1024" "WB-203" }
HS{ } est le littéral vide. Comme les autres littéraux Factor, c'est un objet partagé : toutes les références à HS{ } dans le code source pointent vers le même ensemble. HS{ } clone te donne une nouvelle copie indépendante à chaque appel.
>hash-set ( seq -- set ) ! build a fresh set from a sequence
Quand tu as déjà les valeurs dans une séquence, >hash-set les convertit en une seule étape, en supprimant les doublons.
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
Ces trois mots modifient l'ensemble en place ; aucun ne renvoie quoi que ce soit sur la pile.
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 est la forme groupée : il ajoute chaque élément d'une séquence, en ignorant les doublons exactement comme adjoin le fait un par un.
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? est le test d'appartenance du protocole des ensembles. Il est distinct de member? (de sequences), qui effectue un parcours linéaire d'une séquence. Pour un hash-set, in? est une recherche dans une table de hachage : O(1) en moyenne, tout l'intérêt d'utiliser un hash-set.
null? indique si l'ensemble ne contient aucun élément : c'est la façon directe de demander « est-ce vide ? » plutôt que de comparer cardinality à zéro.
: 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 correspond à « tous les éléments de l'un ou de l'autre » ; intersect à « les éléments présents dans les deux » ; diff à « les éléments de set1 qui ne sont pas dans set2 ». Chacune renvoie un nouvel ensemble sans modifier ses entrées.
set-like convertit set vers le type de exemplar. Les implémentations par défaut de union/intersect/diff l'utilisent pour que le résultat ait le type du récepteur : <my-set> <hash-set> union renvoie un my-set, et non un hash-set.
Les hash-sets vont naturellement de pair avec les tables de hachage pour le parcours de graphe : une table de hachage associe chaque nœud à ses voisins, et un hash-set garde la trace des nœuds déjà visités pour que la recherche ne boucle pas et ne répète pas de travail. Le parcours est alors une file plus ces deux structures, modifiées en place au fur et à mesure que l'on progresse vers l'extérieur.
Tu es le gardien du phare de Cape Crozier. Depuis la salle de la lanterne, tu consignes chaque navire qui passe, chacun identifié par un indicatif unique, et tu coordonnes aussi les relais de signaux avec les autres phares tout au long de la côte.
Définis empty-log pour renvoyer un hash-set vide tout neuf, prêt à recueillir des indicatifs.
empty-log .
! => HS{ }
Définis sight pour qu'il prenne un journal de bord et un indicatif, et consigne l'observation sur place. Ne renvoie rien.
empty-log dup "NS-1024" sight .
! => HS{ "NS-1024" }
Définis seen? pour qu'il prenne un journal de bord et un indicatif, et renvoie t si l'indicatif a été consigné, f sinon.
HS{ "NS-1024" "WB-203" } "NS-1024" seen? . ! => t
HS{ "NS-1024" "WB-203" } "X-99" seen? . ! => f
Définis forget-sighting pour qu'il prenne un journal de bord et un indicatif, et retire l'indicatif du journal sur place. Ne renvoie rien. Si l'indicatif n'y est pas, ne fais rien.
HS{ "NS-1024" "WB-203" } clone dup "WB-203" forget-sighting .
! => HS{ "NS-1024" }
Définis unique-count pour renvoyer le nombre d'indicatifs distincts dans le journal.
HS{ "NS-1024" "WB-203" "AC-77" } unique-count . ! => 3
empty-log unique-count . ! => 0
Les garde-côtes tiennent une relay-map : une hashtable indexée par nom de phare, chaque valeur étant un tableau des phares vers lesquels celui qui sert de clé peut relayer directement.
Définis reachable pour qu'il prenne un phare start et une relay-map, et renvoie un hash-set de tous les phares accessibles depuis start (y compris start lui-même) par relais successifs.
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" }
La paire Far-Isle/Lonely forme sa propre composante connexe, donc aucun des deux n'apparaît dans le résultat.
Inscris-toi sur Exercism pour apprendre et maîtriser Factor avec 47 concepts163 exercices, et un vrai mentorat humain, le tout gratuitement.