Correspondência de tópico de assinatura do MQTT


10

fundo

MQTT (Message Queuing Telemetry Transport) é um baseada-subscribe publicar protocolo de mensagens padrão ISO ( Wikipedia ).

Cada mensagem tem um tópico, como os seguintes exemplos:

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

Os clientes MQTT podem assinar tópicos de mensagens usando curingas:

  • Nível único: +
  • Todos os níveis em diante: #

Por exemplo, a assinatura myhome/groundfloor/+/temperatureproduziria estes resultados (não conformidades em negrito ):

✅ myhome / térreo / sala / temperatura
✅ myhome / térreo / cozinha / temperatura
❌ myhome / térreo / sala / brilho
❌ myhome / firstfloor / sala / temperatura
garagem / térreo / frigorífico / temperatura

Considerando que a assinatura +/groundfloor/#produziria estes resultados:

✅ myhome / térreo / sala / temperatura
✅ myhome / térreo / cozinha / brilho
✅ garagem / térreo / geladeira / temperatura / mais / específico / campos
❌ myhome / primeiro andar / sala / temperatura /
❌ myhome / porão / canto / temperatura

Mais informações aqui .

A tarefa

Implemente uma função / programa aceitando duas strings e retornando um booleano. A primeira string é o tópico do assunto, a segunda é o tópico do critério. O tópico de critérios usa a sintaxe de assinatura detalhada acima. A função é verdadeira quando o assunto corresponde aos critérios.

Regras para esta tarefa:

  • Os tópicos são ASCII
  • Não há campos de critérios além do #curinga
  • Os curingas não aparecem nos tópicos do assunto
  • Número de campos de assunto> = número de campos de critérios
  • Não há campos com 0 caracteres nem barras iniciais ou finais

Casos de teste

critérios1 = "myhome / piso térreo / + / temperatura"
critérios2 = "+ / piso térreo / #"

("abc", "ab") => false
("abc", "abc") => true
("abc / de", "abc") => false
("myhome / groundfloor / livingroom / temperature", critérios1 ) => true
("myhome / piso térreo / cozinha / temperatura", critério1) => true
("myhome / piso térreo / sala de estar / brilho", critério1) => false
("myhome / piso térreo / sala de estar / temperatura", critério1) = > false
("garagem / piso térreo / geladeira / temperatura", critério1) => false
("casa / piso térreo / sala / temperatura", critério2) => true
("casa / piso térreo / cozinha / brilho", critério2) => true
("garagem / térreo / geladeira / temperatura / mais / específico / campos ", critérios2) => verdadeiro
(" myhome / primeiro andar / sala / temperatura ", critérios2) => falso
("myhome / porão / canto / temperatura", critérios2) => falso
("música / kei $ ha / mais recente", "+ / kei $ ha / +") => verdadeiro


@HyperNeutrino, essa é uma boa pergunta. Estou em cima do muro. O assunto a/b/cnão corresponderia aos critérios a/b, por isso estou inclinado a dizer não .
22419 Patrick Patrick

4
/ / + E # garantem que nunca apareçam nas partes do tópico?
Jonathan Allan

Vejo no blog vinculado que "Além disso, apenas a barra é um tópico válido", mas nenhuma menção a + e #, então acho que esses dois podem ser.
Jonathan Allan

11
@JonathanAllan De docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/... : Os caracteres curinga podem ser usados em Tópico Filters, mas não deve ser usado dentro de um nome de tópico
Nick Kennedy

2
@NickKennedy - boa escavação, mas realmente não precisamos.
Jonathan Allan

Respostas:


3

Gelatina , 20 bytes

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

Um link monádico que aceita uma lista de listas de caracteres [topic, pattern], que retorna 1ou 0para correspondência ou não, respectivamente.

Experimente online! Ou veja uma suíte de testes .

Como?

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

2

Ruby , 65 bytes

Solução Regex. Eu adicionei Regex.escapeno caso de um nome de critério ser algo parecido com.java/string[]/\nou bobo que teria partes de expressões regulares.

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

Experimente online!

Solução não regex, 77 bytes

Utiliza uma boa e simples técnica de divisão, zip e correspondência. Eu desenvolvi esse primeiro antes de perceber que mesmo com Regex.escapea solução regex teria sido menor de qualquer maneira.

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

Experimente online!


.*?deve funcionar no lugar de [^/]*.
Fund Monica's Lawsuit

@NicHartley que irá desencadear uma falsa partida para os critérios a/+/dcom temaa/b/c/d
valor da tinta

Ah, assim será. Agrupar isso em um grupo atômico corrige isso, mas depois é dois bytes mais longo. Ah bem.
Fund Monica's Lawsuit


1

Python 3 , 72 bytes

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

Experimente online!

Esse problema pode ser simplificado trivialmente para uma correspondência de regex, embora outro método mais interessante possa produzir melhores resultados.

Edição Eu vim com uma solução de 107 bytes que não usa regex. Eu não sei se ele pode ficar menor que 72 ou talvez eu simplesmente não esteja tentando corrigir a abordagem. Apenas a estrutura de zip dividido parece ser muito grande. Experimente Online!


2
Se a sequência contiver outros caracteres regex, isso poderá falhar. Eu cuidaria disso, embora nenhum dos casos de teste atuais contenha algo remotamente parecido com regex.
valor é de R $

... como f('myhome/ground$floor/livingroom/temperature', 'myhome/ground$floor/+/temperature')que falha
Jonathan Allan

Como o Value Ink diz, +/kei$ha/+não corresponde 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('/')))

Experimente online!

Agradecemos a Jonathan Allan e a Value Ink por apontar bugs.


Dá a resposta errada f('ab', 'abc').
valor é de R $

@ Jonathan Allan: Na verdade, as regras dizem 'Número de campos de assunto> = número de campos de critérios'. Mas outros problemas precisava ser consertado ...
Chas Brown

Oh regra estranha dado o contexto do 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

Experimente online!

Edit: -4 bytes graças a @cole.


11
a#b=a==bparece funcionar para alguns bytes menos, a menos que eu estou faltando alguma coisa
cole

@ cole: sim, isso funciona. Muito obrigado!
nimi

1

Clojure , 107 91 76 65 102 bytes

Uma função anônima, retorna o tópico do assunto nilcomo verdade e como falsey (válido em Clojure).

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

107 102 trabalhando
91 76 65 todos derrotados com caracteres regex


... e meu comentário sob a sua pergunta se torna pertinente #
Jonathan Allan

@JonathanAllan, de fato, exceto + e # não aparecem em cordas tópico assunto :)
Patrick

Eu acho que isso falha por assunto music/kei$ha/lateste critérios +/kei$ha/+(que devem corresponder e é ASCII válido).
Chas Brown

@ChasBrown, correto e com ^ em vez de $; obrigado.
22419 Patrick Patrick

11
Tente com '\ Q' antes e '\ E' após o padrão anterior à substituição - fonte
Jonathan Allan


0

Python 3, 99 88 bytes

Sem usar uma regex. Com alguma ajuda de Jonathan Allan e 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:])

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:])salva 12. No entanto, este não consegue processar alguns casos extremos, como f('abc/ijk/x', 'abc/+/xyz')ou f('abc/ijk/xyz', 'abc/+/x'), o que pode ser corrigido comf=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

Isso falha para f('abc','ab')e f('abc/de','abc')(ambos devem retornar False, mas há um IndexError).
Chas Brown

...or p[:1]in(s[:1],'+')and...corrige os casos de borda @ChasBrown e apontei para um custo de 2 bytes.
Jonathan Allan

Falha em outro caso de borda de um '+' à direita (por exemplo f('a/b', 'a/+')), mas corrigível em 0 bytes com ...or(s[:1]in'/')*2:]).
Jonathan Allan

Experimente online sempre é recomendado!
Chas Brown

0

Carvão , 36 bytes

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

Experimente online! Link é a versão detalhada do código. Saídas -(saída implícita do carvão vegetal para true) para uma correspondência, nada para nenhuma correspondência. Explicação:

≔⪪S/θ

Divida o assunto em /s.

≔⪪S/η

Divida os critérios em /s.

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

Se o critério contiver (isto é, termina com) a #, remova-o e apare o assunto com o novo tamanho do critério.

F⌕Aη+§≔θι+

Onde os critérios contiverem +, substitua esse elemento no assunto por +.

⁼θη

Compare o assunto com os critérios e imprima implicitamente o resultado.


0

Retina 0.8.2 , 42 bytes

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

Experimente online! Explicação:

%`$
/

Sufixo a /às duas linhas.

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

Remova repetidamente o primeiro elemento do assunto e dos critérios enquanto eles são iguais ou o elemento de critérios é um (feliz) +.

^¶$|¶#/$

O critério corresponde se for apenas um #(com o /que foi adicionado anteriormente), caso contrário, o assunto e o critério devem estar vazios nesse ponto.



0

Geléia , 22 19 bytes

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

Experimente online!

Um link monádico que assume como argumento [topic], [criterion]e retorna 1para uma correspondência e 0sem correspondência.


0

JavaScript, 69 66 bytes

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

Experimente online!


Isso falha no assunto music/kei$ha/lateste nos critérios +/kei$ha/+(que devem corresponder e é 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]!='#')

Experimente online!


0

05AB1E , 21 bytes

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

Insira como uma lista na ordem [criteria, topic].

Experimente online ou verifique todos os casos de teste .

Explicação:

ε                      # 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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.