Coincidencia de tema de suscripción MQTT

10

Antecedentes

MQTT (Message Queuing Telemetry Transport) es un protocolo de mensajería basado en publicación, suscripción y estándar ISO ( Wikipedia ).

Cada mensaje tiene un tema, como los siguientes ejemplos:

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

Los clientes MQTT pueden suscribirse a los temas de mensajes utilizando comodines:

  • Nivel único: +
  • Todos los niveles en adelante: #

Por ejemplo, la suscripción myhome/groundfloor/+/temperatureproduciría estos resultados (no conformidades en negrita ):

✅ myhome / planta baja / sala de estar / temperatura
✅ myhome / planta baja / cocina / temperatura
❌ myhome / planta baja / sala de estar / brillo
❌ myhome / primer piso / sala de estar / temperatura
garaje / planta baja / refrigerador / temperatura

Mientras que la suscripción +/groundfloor/#produciría estos resultados:

✅ myhome / planta baja / sala de estar / temperatura
✅ myhome / planta baja / cocina / brillo
✅ garaje / planta baja / refrigerador / temperatura / más / específico / campos
❌ myhome / primer piso / sala de estar / temperatura
❌ myhome / sótano / esquina / temperatura

Más información aquí .

La tarea

Implemente una función / programa que acepte dos cadenas y devuelva un valor booleano. La primera cadena es el tema del tema, la segunda es el tema del criterio. El tema de criterios utiliza la sintaxis de suscripción detallada anteriormente. La función es verdadera cuando el sujeto coincide con los criterios.

Reglas para esta tarea:

  • Los temas son ASCII
  • No hay campos de criterios más allá del #comodín
  • Los comodines no aparecen en los temas
  • Número de campos de asunto> = número de campos de criterios
  • No hay campos de 0 caracteres ni barras inclinadas hacia adelante o hacia atrás

Casos de prueba

criterios1 = "mi casa / planta baja / + / temperatura"
criterios2 = "+ / planta baja / #"

("abc", "ab") => falso
("abc", "abc") => verdadero
("abc / de", "abc") => falso
("mi casa / planta baja / sala de estar / temperatura", criterio1 ) => verdadero
("mi casa / planta baja / cocina / temperatura", criterio1) => verdadero
("mi casa / planta baja / sala de estar / brillo", criterio1) => falso
("mi casa / primer piso / sala de estar / temperatura", criterio1) = > falso
("garaje / planta baja / refrigerador / temperatura", criterio1) => falso
("mi casa / planta baja / sala de estar / temperatura", criterio2) => verdadero
("mi casa / planta baja / cocina / brillo", criterio2) => verdadero
("garaje / planta baja / refrigerador / temperatura / más / específico / campos ", criterio2) => verdadero
(" mi casa / primer piso / sala de estar / temperatura ", criterio2) => falso
("myhome / sótano / esquina / temperatura", criterio2) => falso
("music / kei $ ha / latest", "+ / kei $ ha / +") => true

Patricio
fuente
@HyperNeutrino, esa es una buena pregunta. Estoy en la cerca. El tema a/b/cno coincidía con los criterios a/b, por lo que me inclino a decir que no .
Patrick
44
¿Se garantiza que /, + y # nunca aparecerán en las partes del tema?
Jonathan Allan
Veo en el blog vinculado que "Además, la barra diagonal sola es un tema válido" pero no se menciona + y #, así que supongo que estos dos pueden serlo.
Jonathan Allan
1
@JonathanAllan De docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/… : Los caracteres comodín se pueden usar en Filtros de tema, pero NO DEBEN usarse dentro de un Nombre de tema
Nick Kennedy
2
@NickKennedy: buena excavación, pero realmente no deberíamos necesitarlo.
Jonathan Allan

Respuestas:

3

Jalea , 20 bytes

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

Un enlace monádico que acepta una lista de listas de caracteres [topic, pattern], que devuelve 1o 0para coincidencia o no coincidencia respectivamente.

Pruébalo en línea! O ver un conjunto de pruebas .

¿Cómo?

ṣ€”/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
fuente
2

Ruby , 65 bytes

Solución de expresiones regulares. Agregué Regex.escapeen caso de que un nombre de criterio sea algo así como com.java/string[]/\nalgo tonto que tendría piezas de expresiones regulares.

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

Pruébalo en línea!

Solución no regex, 77 bytes

Utiliza una bonita técnica simple de división, compresión y combinación. Desarrollé este primero antes de darme cuenta de que incluso con Regex.escapela solución regex habría sido más corta de todos modos.

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

Pruébalo en línea!

Tinta de valor
fuente
.*?debería funcionar en lugar de [^/]*.
Financia la demanda de Mónica
@NicHartley que desencadenará una coincidencia falsa para los criterios a/+/dcon el temaa/b/c/d
Value Ink
Ah, así será. Ajustar eso en un grupo atómico lo arregla, pero luego es dos bytes más largo. Oh bien.
Financia la demanda de Mónica el
2

Perl 5 -pl , 50 bytes

$_="\Q$_";s|\\\+|[^/]+|g;s/\\\#/.*/;$_=<>=~m|^$_$|

Pruébalo en línea!

Xcali
fuente
pequeña mejora, <>=~/^$_$/al final
Nahuel Fouilleul
1

Python 3 , 72 bytes

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

Pruébalo en línea!

Este problema puede simplificarse trivialmente a una coincidencia de expresiones regulares, aunque otro método más interesante puede producir mejores resultados.

EDITAR Se me ocurrió una solución de 107 bytes que no usa expresiones regulares. No sé si puede ser más corto que 72 o tal vez simplemente no estoy tratando de corregir el enfoque de esto. Sin embargo, solo la estructura de cremallera dividida parece ser demasiado grande. ¡Pruébelo en línea!

Hiperneutrino
fuente
2
Si la secuencia contiene otros caracteres regex, esto podría fallar. Lo buscaría aunque ninguno de los casos de prueba actuales contenga algo remotamente parecido a expresiones regulares.
Value Ink el
... como el f('myhome/ground$floor/livingroom/temperature', 'myhome/ground$floor/+/temperature')que falla
Jonathan Allan
Como dice Value Ink, +/kei$ha/+no coincide music/kei$ha/latest.
Chas Brown
1

Python 2 , 85 84 80 92 89 bytes

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

Pruébalo en línea!

Gracias a Jonathan Allan y Value Ink por señalar errores.

Chas Brown
fuente
Da la respuesta incorrecta f('ab', 'abc').
Value Ink el
@ Jonathan Allan: En realidad, las reglas dicen 'Número de campos de asunto> = número de campos de criterios'. Pero otros problemas requieren fijación ...
Chas Brown
¡Oh regla extraña dado el contexto del problema!
Jonathan Allan
1

Haskell, 76 73 71 67 bytes

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

Pruébalo en línea!

Editar: -4 bytes gracias a @cole.

nimi
fuente
1
a#b=a==bParece que funciona para unos pocos bytes menos, a no ser que me falta algo
cole
@cole: sí, eso funciona. ¡Muchas gracias!
nimi
1

Clojure , 107 91 76 65 102 bytes

Una función anónima, devuelve el tema del tema como verdadero y nilfalso (válido en Clojure).

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

107 102 trabajando
91 76 65 todos derrotados con caracteres regex

Patricio
fuente
... y mi comentario bajo su pregunta se vuelve pertinente
Jonathan Allan
@JonathanAllan, de hecho, excepto que + y # no aparecen en las cadenas temáticas del tema :)
Patrick
Creo que esto falla por tema music/kei$ha/latesty criterios +/kei$ha/+(que deben coincidir y es ASCII válido).
Chas Brown
@ChasBrown, correcto y con ^ en lugar de $; Gracias.
Patrick
1
Pruebe con '\ Q' antes y '\ E' después del patrón antes del reemplazo - fuente
Jonathan Allan
0

Kotlin , 106 bytes

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

Pruébalo en línea!

Quinn
fuente
0

Python 3, 99 88 bytes

Sin usar una expresión regular. Con ayuda de Jonathan Allan y 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
fuente
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:])ahorra 12. Sin embargo, esto no puede procesar algunos casos extremos como f('abc/ijk/x', 'abc/+/xyz')o f('abc/ijk/xyz', 'abc/+/x'), que se pueden solucionar conf=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
Esto falla para f('abc','ab')y f('abc/de','abc')(ambos deberían regresar False, pero en su lugar hay un IndexError).
Chas Brown
...or p[:1]in(s[:1],'+')and...corrige los casos extremos @ChasBrown y señalé por un costo de 2 bytes.
Jonathan Allan
Falla otro caso de borde de un '+' final (por ejemplo f('a/b', 'a/+')) pero reparable en 0 bytes con ...or(s[:1]in'/')*2:]).
Jonathan Allan
¡Pruébelo en línea siempre se recomienda!
Chas Brown
0

Carbón , 36 bytes

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

Pruébalo en línea! El enlace es a la versión detallada del código. Salidas -(salida implícita de carbón para true) para una coincidencia, nada para ninguna coincidencia. Explicación:

≔⪪S/θ

Dividir el tema en /s.

≔⪪S/η

Dividir los criterios en /s.

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

Si el criterio contiene (es decir, termina con) a #, elimínelo y recorte el tema a la nueva longitud del criterio.

F⌕Aη+§≔θι+

Donde el criterio contiene +, reemplace ese elemento en el tema con +.

⁼θη

Compare el tema con los criterios e imprima implícitamente el resultado.

Neil
fuente
0

Retina 0.8.2 , 42 bytes

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

Pruébalo en línea! Explicación:

%`$
/

Sufijo a /a ambas líneas.

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

Repetidamente elimine el primer elemento de sujeto y criterio mientras son iguales o el elemento de criterio es un (feliz) +.

^¶$|¶#/$

El criterio coincide si es solo un #(con el /que se agregó anteriormente) de lo contrario, tanto el sujeto como los criterios deberían estar vacíos en este punto.

Neil
fuente
0

Jalea , 22 19 bytes

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

Pruébalo en línea!

Un enlace monádico que toma como argumento [topic], [criterion]y vuelve 1para una coincidencia y 0para ninguna coincidencia.

Nick Kennedy
fuente
0

JavaScript, 69 66 bytes

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

Pruébalo en línea!

Darrylyeo
fuente
Esto falla para el tema music/kei$ha/latesty los criterios +/kei$ha/+(que deben coincidir y es ASCII válido).
Chas Brown
0

Python 3 , 149 148 bytes

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]!='#')

Pruébalo en línea!

Dat
fuente
0

05AB1E , 21 bytes

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

Ingrese como una lista en el orden [criteria, topic].

Pruébelo en línea o verifique todos los casos de prueba .

Explicación:

ε                      # 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
fuente