Correspondance de sujet d'abonnement MQTT

10

Contexte

MQTT (Message Queuing Telemetry Transport) est un protocole de messagerie basé sur la publication-abonnement ( Wikipedia ) standard ISO .

Chaque message a un sujet, comme les exemples suivants:

  • myhome/groundfloor/livingroom/temperature
  • USA/California/San Francisco/Silicon Valley
  • 5ff4a2ce-e485-40f4-826c-b1a5d81be9b6/status
  • Germany/Bavaria/car/2382340923453/latitude

Les clients MQTT peuvent s'abonner aux rubriques de message à l'aide de caractères génériques:

  • Niveau unique: +
  • Tous les niveaux à partir de: #

Par exemple, l'abonnement myhome/groundfloor/+/temperatureproduirait ces résultats (non-conformités en gras ):

✅ myhome / rez-de-chaussée / salon / température
✅ myhome / rez-de-chaussée / cuisine / température
❌ myhome / rez-de-chaussée / salon / luminosité
❌ myhome / rez -de- chaussée / salon / température
garage / rez-de-chaussée / réfrigérateur / température

Alors que l'abonnement +/groundfloor/#produirait ces résultats:

✅ myhome / rez-de-chaussée / salon / température
✅ myhome / rez-de-chaussée / cuisine / luminosité
✅ garage / rez-de-chaussée / réfrigérateur / température / plus / spécifique / champs
❌ myhome / premier étage / salon / température
❌ myhome / sous - sol / coin / température

Plus d'infos ici .

La tâche

Implémentez une fonction / un programme acceptant deux chaînes et renvoyant un booléen. La première chaîne est le sujet sujet, la seconde est le sujet critère. La rubrique Critères utilise la syntaxe d'abonnement détaillée ci-dessus. La fonction est vraie lorsque le sujet correspond aux critères.

Règles pour cette tâche:

  • Les sujets sont ASCII
  • Il n'y a pas de champs de critères au-delà du #caractère générique
  • Les caractères génériques n'apparaissent pas dans les sujets
  • Nombre de champs thématiques> = nombre de champs critères
  • Il n'y a aucun champ de 0 caractère ni barre oblique avant ou arrière

Cas de test

critères1 = "ma maison / rez-de-chaussée / + / température"
critères2 = "+ / rez-de-chaussée / #"

("abc", "ab") => false
("abc", "abc") => true
("abc / de", "abc") => false
("myhome / rez-de-chaussée / salon / température", critère1 ) => true
("myhome / rez-de-chaussée / cuisine / température", critères1) => true
("myhome / rez-de-chaussée / salon / luminosité", critères1) => false
("myhome / rez-de-chaussée / salon / température", critères1) = > false
("garage / rez-de-chaussée / réfrigérateur / température",
critère1 ) => faux ("myhome / rez-de-chaussée / salon / température",
critère2 ) => true ("myhome / rez-de-chaussée / cuisine / luminosité", critère2) => true
("garage / rez-de-chaussée / réfrigérateur / température / plus / spécifique / champs ",
critère2 ) => vrai (" ma maison / rez-de-chaussée / salon / température ", critère2) => faux
("ma maison / sous-sol / coin / température", critère2) => faux
("musique / kei $ ha / dernier", "+ / kei $ ha / +") => vrai

Patrick
la source
@HyperNeutrino, c'est une bonne question. Je suis sur la clôture. Le sujet a/b/cne correspondrait pas aux critères a/b, donc j'ai tendance à dire non .
Patrick
4
Est-il garanti que /, + et # n'apparaîtront jamais dans les parties du sujet?
Jonathan Allan
Je vois dans le blog lié que "De plus, la barre oblique seule est un sujet valide" mais aucune mention de + et #, donc je suppose que ces deux peuvent l'être.
Jonathan Allan
1
@JonathanAllan De docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/… : Les caractères génériques peuvent être utilisés dans les filtres de sujet, mais NE DOIVENT PAS être utilisés dans un nom de sujet
Nick Kennedy
2
@NickKennedy - bien creuser, mais nous ne devrions vraiment pas en avoir besoin.
Jonathan Allan

Réponses:

3

Gelée , 20 octets

ṣ€”/ZṖF”#eƊ¿œiÐḟ”+ZE

Un lien monadique acceptant une liste de listes de caractères [topic, pattern], qui renvoie respectivement 1ou 0pour match ou no-match.

Essayez-le en ligne! Ou consultez une suite de tests .

Comment?

ṣ€”/ZṖF”#eƊ¿œiÐḟ”+ZE - Link: list of lists of characters, [topic, pattern]
 €                   - for each:
ṣ                    -   split at occurrences of:
  ”/                 -     '/' character
    Z                - transpose (any excess of topic is kept)
           ¿         - while...
          Ɗ          - ...condition: last three links as a monad:
       ”#            -   '#' character
         e           -   exists in:
      F              -     flatten
     Ṗ               - ...do: pop the tail off
              Ðḟ     - filter discard those for which:
            œi       -   first multi-dimensional index of: ([] if not found, which is falsey)
                ”+   -     '+' character
                  Z  - transpose
                   E - all equal?
Jonathan Allan
la source
2

Ruby , 65 octets

Solution Regex. J'ai ajouté Regex.escapeau cas où un nom de critère se com.java/string[]/\ntrouverait être quelque chose comme ou quelque chose de stupide qui aurait des morceaux d'expression régulière.

->s,c{s=~/^#{Regexp.escape(c).sub('\#','.*').gsub'\+','[^/]*'}$/}

Essayez-le en ligne!

Solution non regex, 77 octets

Utilise une technique simple de split, zip et match. J'ai d'abord développé celui-ci avant de réaliser que même avec Regex.escapela solution regex, elle aurait été de toute façon plus courte.

->s,c{s.split(?/).zip(c.split ?/).all?{|i,j|i==j||'+#'[j||9]||!j&&c[-1]==?#}}

Essayez-le en ligne!

Encre de valeur
la source
.*?devrait fonctionner à la place de [^/]*.
Fund Monica's Lawsuit
@NicHartley qui déclenchera une fausse correspondance pour les critères a/+/davec le sujeta/b/c/d
Value Ink
Ah, il en sera ainsi. Envelopper cela dans un groupe atomique corrige cela, mais c'est deux octets de plus. Tant pis.
Fund Monica's Lawsuit
1

Python 3 , 72 octets

lambda a,b:bool(re.match(b.translate({43:"[^/]+",35:".+"}),a))
import re

Essayez-le en ligne!

Ce problème peut être simplifié de manière triviale en une correspondance d'expression régulière, bien qu'une autre méthode plus intéressante puisse produire de meilleurs résultats.

EDIT J'ai trouvé une solution de 107 octets n'utilisant pas regex. Je ne sais pas si cela peut devenir plus court que 72 ou peut-être que je ne cherche tout simplement pas à corriger l'approche à ce sujet. Cependant, la structure à zip divisé semble trop grande. Essayez-le en ligne!

HyperNeutrino
la source
2
Si la séquence contient d'autres caractères regex, cela peut échouer. Je ferais attention à cela même si aucun des cas de test actuels ne contient quoi que ce soit à distance comme des regex.
Value Ink
... comme f('myhome/ground$floor/livingroom/temperature', 'myhome/ground$floor/+/temperature')ce qui échoue
Jonathan Allan
Comme le dit Value Ink, +/kei$ha/+ne correspond pas music/kei$ha/latest.
Chas Brown
1

Python 2 , 85 84 80 92 89 octets

lambda s,c:all(x in('+','#',y)for x,y in zip(c.split('/')+[0]*-c.find('#'),s.split('/')))

Essayez-le en ligne!

Merci à Jonathan Allan et Value Ink d' avoir signalé des bugs.

Chas Brown
la source
Donne la mauvaise réponse f('ab', 'abc').
Value Ink
@Jonathan Allan: En fait, les règles disent 'Nombre de champs sujets> = nombre de champs critères'. Mais d'autres problèmes devaient être résolus ...
Chas Brown
Oh règle étrange étant donné le contexte du problème!
Jonathan Allan
1

Haskell, 76 73 71 67 octets

(a:b)#(c:d)=a=='+'&&b#snd(span(/='/')d)||a=='#'||a==c&&b#d
a#b=a==b

Essayez-le en ligne!

Edit: -4 octets grâce à @cole.

nimi
la source
1
a#b=a==bsemble fonctionner pour quelques octets de moins, sauf si je manque quelque chose
Cole
@cole: oui, ça marche. Merci beaucoup!
nimi
1

Clojure , 107 91 76 65 102 octets

Une fonction anonyme, renvoie le sujet comme véridique et nilcomme falsey (valable dans Clojure).

(defn ?[t c](every? #(#{"#""+"(% 0)}(% 1))(apply #(map vector % %2)(map #(re-seq #"[^/]+" %) [t c]))))

107 102 fonctionnant
91 76 65 tous vaincus avec des caractères regex

Patrick
la source
... et mon commentaire sous votre question devient pertinent
Jonathan Allan
@JonathanAllan, en effet, sauf que + et # n'apparaissent pas dans les chaînes de sujet :)
Patrick
Je pense que cela échoue pour le sujet music/kei$ha/latestet les critères +/kei$ha/+(qui devraient correspondre et sont valides ASCII).
Chas Brown
@ChasBrown, correct et avec ^ au lieu de $; Merci.
Patrick
1
Essayez avec '\ Q' avant et '\ E' après le motif avant le remplacement - source
Jonathan Allan
0

Kotlin , 106 octets

fun f(s:List<String>)=s[1].split("/").filterIndexed{i,v->v!="+"&&v!="#"&&v!=s[0].split("/")[i]}.count()==0

Essayez-le en ligne!

Quinn
la source
0

Python 3, 99 88 octets

Sans utiliser d'expression régulière. Avec l'aide de Jonathan Allan et Chas Brown.

f=lambda s,p:p in(s,'#')or p[:1]in(s[:1],'+')and f(s[1:],p['+'!=p[:1]or(s[:1]in'/')*2:])
RootTwo
la source
f=lambda s,p:s==p or'#'==p[0]or p[0]in(s[0]+'+')and f(s[1:],p['+'!=p[0]or(s[0]=='/')*2:])enregistre 12. Cependant, cela ne parvient pas à traiter certains cas marginaux comme f('abc/ijk/x', 'abc/+/xyz')ou f('abc/ijk/xyz', 'abc/+/x'), qui peuvent être corrigés avecf=lambda s,p:s==p or'#'==p[:1]or p[:1]in(s[:1]+'+')and f(s[1:],p['+'!=p[:1]or(s[:1]=='/')*2:])
Jonathan Allan
Cela échoue pour f('abc','ab')et f('abc/de','abc')(les deux devraient revenir False, mais à la place il y a un IndexError).
Chas Brown
...or p[:1]in(s[:1],'+')and...corrige les cas marginaux @ChasBrown et je l'ai souligné pour un coût de 2 octets.
Jonathan Allan
Échoue un autre cas de bord d'un «+» de fin (par exemple f('a/b', 'a/+')) mais réparable en 0 octet avec ...or(s[:1]in'/')*2:]).
Jonathan Allan
L'essayer en ligne est toujours recommandé!
Chas Brown
0

Fusain , 36 octets

≔⪪S/θ≔⪪S/ηF∧№η#⊟η≔…θLηθF⌕Aη+§≔θι+⁼θη

Essayez-le en ligne! Le lien est vers la version détaillée du code. Sorties -(sortie implicite de Charcoal pour true) pour une correspondance, rien pour aucune correspondance. Explication:

≔⪪S/θ

Séparez le sujet à l' /art.

≔⪪S/η

Divisez les critères relatifs à l' /art.

F∧№η#⊟η≔…θLηθ

Si le critère contient (c'est-à-dire se termine par) a, #supprimez-le et ajustez le sujet à la nouvelle longueur des critères.

F⌕Aη+§≔θι+

Lorsque le critère contient, +remplacez cet élément du sujet par +.

⁼θη

Comparez le sujet avec les critères et imprimez implicitement le résultat.

Neil
la source
0

Retina 0.8.2 , 42 octets

%`$
/
+`^([^/]+/)(.*¶)(\1|\+/)
$2
^¶$|¶#/$

Essayez-le en ligne! Explication:

%`$
/

Suffixe a /aux deux lignes.

+`^([^/]+/)(.*¶)(\1|\+/)
$2

Supprimez à plusieurs reprises le premier élément du sujet et des critères pendant qu'ils sont égaux ou que l'élément de critère est (heureux) +.

^¶$|¶#/$

Les critères correspondent si c'est juste un #(avec celui /qui a été ajouté plus tôt) sinon le sujet et les critères doivent être vides à ce stade.

Neil
la source
0

Gelée , 22 19 octets

ḟ”+ṣ”/)ZẠƇṖœi”#$¿ZE

Essayez-le en ligne!

Un lien monadique qui prend comme argument [topic], [criterion]et renvoie 1pour une correspondance et 0pour aucune correspondance.

Nick Kennedy
la source
0

JavaScript, 69 66 octets

t=>s=>new RegExp(s.split`+`.join`[^/]+`.split`#`.join`.+`).test(t)

Essayez-le en ligne!

darrylyeo
la source
Cela échoue pour le sujet music/kei$ha/latestet les critères +/kei$ha/+(qui doivent correspondre et sont des ASCII valides).
Chas Brown
0

Python 3 , 149 148 octets

def f(t,c):t,c=t.split('/'),c.split('/');return all([c[i]=='+'or c[i]==t[i]or c[i]=='#'for i in range(len(c))])and not(len(c)!=len(t)and c[-1]!='#')

Essayez-le en ligne!

Dat
la source
0

05AB1E , 21 octets

ε'/¡}ζʒ'+å≠}˜'#¡н2ôøË

Saisie sous forme de liste dans l'ordre [criteria, topic].

Essayez-le en ligne ou vérifiez tous les cas de test .

Explication:

ε                      # Map both strings in the implicit input-list to:
 '/¡                  '#  Split the string on "/"
                       #   i.e. ["+/+/A/B/#","z/y/A/B/x/w/v/u"]
                       #    → [["+","+","A","B","#"],["z","y","A","B","x","w","v","u"]]
                     # After the map: zip/transpose the two string-lists,
                       # with space as (default) filler
                       #  → [["+","z"],["+","y"],["A","A"],["B","B"],["#","x"],[" ","w"],
                       #     [" ","v"],[" ","u"]]
      ʒ    }           # Filter each pair by:
       '+å≠           '#  Only keep those which do NOT contain a "+"
                       #   → [["A","A"],["B","B"],["#","x"],[" ","w"],[" ","v"],[" ","u"]]
            ˜          # Flatten the filtered list
                       #  → ["A","A","B","B","#","x"," ","w"," ","v"," ","u"]
             '#¡      '# Split the list by "#"
                       #  → [["A","A","B","B"],["x"," ","w"," ","v"," ","u"]]
                н      # Only keep the first part
                       #  → ["A","A","B","B"]
                 2ô    # Split this back into pairs of two
                       #  → [["A","A"],["B","B"]]
                   ø   # Zip/transpose them back
                       #  → [["A","B"],["A","B"]]
                    Ë  # And check if both inner lists are equal
                       #  → 1 (truthy)
                       # (after which the result is output implicitly)
Kevin Cruijssen
la source