Domanda

Diciamo che ho il seguente:

  

{{Hello | Hi | Hey} {world | earth} |   {Arrivederci | addio} {noobs | n3wbz | n00blets}}

E voglio che si trasformi in uno dei seguenti:

Hello world 
Goodbye noobs 
Hi earth
farewell n3wbz 
// etc.

Prestando attenzione al modo in cui " gira " la sintassi è nidificata. Potrebbe essere nidificato a un miliardo di strati di profondità per quanto ne sappiamo.

Posso farlo facilmente, tranne quando sono nidificati come nell'esempio sopra il mio regex si rovina e i risultati non sono corretti.

Qualcuno potrebbe mostrare un esempio in un linguaggio .NET o Python per favore?

È stato utile?

Soluzione

Un modo semplice con re.subn , che può anche accettare una funzione anziché una stringa di sostituzione:

import re
from random import randint

def select(m):
    choices = m.group(1).split('|')
    return choices[randint(0, len(choices)-1)]

def spinner(s):
    r = re.compile('{([^{}]*)}')
    while True:
        s, n = r.subn(select, s)
        if n == 0: break
    return s.strip()

Sostituisce semplicemente tutte le scelte più profonde che incontra, quindi scorre fino a quando non rimane alcuna scelta. subn restituisce una tupla con il risultato e il numero di sostituzioni effettuate, il che è comodo per rilevare la fine dell'elaborazione.

La mia versione di select () può essere sostituita da quella di Bobince che usa random.choice () ed è più elegante se vuoi solo attenerti a un selettore casuale. Se vuoi costruire un albero di scelta, potresti estendere la funzione sopra, ma avrai bisogno di variabili globali per tenere traccia di dove ti trovi, quindi avrebbe senso spostare le funzioni in una classe. Questo è solo un suggerimento, non svilupperò quell'idea poiché non era proprio la domanda originale.

Nota infine che dovresti usare r.subn (select, s, re.U) se hai bisogno di stringhe unicode ( s = u " {...} " )

Esempio:

>>> s = "{{Hello|Hi|Hey} {world|earth} | {Goodbye|farewell} {noobs|n3wbz|n00blets}}"
>>> print spinner(s)
'farewell n3wbz'

Modifica: Sostituito sub con subn per evitare un ciclo infinito (grazie a Bobince per segnalarlo) e renderlo più efficiente, e sostituito {([^ {}] +)} con {([^ {}] *)} per estrarre anche parentesi graffe vuote. Ciò dovrebbe rendere più robusti i pattern mal formattati.

Per le persone a cui piace mettere il più possibile su una linea (che personalmente non incoraggerei):

def spin(s):
    while True:
        s, n = re.subn('{([^{}]*)}',
                       lambda m: random.choice(m.group(1).split("|")),
                       s)
        if n == 0: break
    return s.strip()

Altri suggerimenti

Dovrebbe essere abbastanza semplice, basta impedire a un set di controventi di includerne un altro, quindi chiamare ripetutamente facendo sostituzioni dalle partite interne verso l'esterno:

def replacebrace(match):
    return random.choice(match.group(1).split('|'))

def randomizebraces(s):
   while True:
       s1= re.sub(r'\{([^{}]*)\}', replacebrace, s)
       if s1==s:
           return s
       s= s1

>>> randomizebraces('{{Hello|Hi|Hey} {world|earth}|{Goodbye|farewell} {noobs|n3wbz|n00blets}}')
'Hey world'
>>> randomizebraces('{{Hello|Hi|Hey} {world|earth}|{Goodbye|farewell} {noobs|n3wbz|n00blets}}')
'Goodbye noobs'

Questo regex inverter utilizza pyparsing per generare stringhe corrispondenti (con alcune restrizioni - non sono ammessi simboli di ripetizione illimitati come + e *). Se sostituisci {} con () per trasformare la tua stringa originale in regex, l'inverter genera questo elenco:

Helloworld
Helloearth
Hiworld
Hiearth
Heyworld
Heyearth
Goodbyenoobs
Goodbyen3wbz
Goodbyen00blets
farewellnoobs
farewelln3wbz
farewelln00blets

(So che gli spazi sono crollati, ma forse questo codice ti darà alcune idee su come attaccare questo problema.)

Vorrei usare re.finditer e costruire un albero di analisi di base per determinare il livello di annidamento. Per farlo, vorrei usare l'attributo span dell'oggetto match regex:

text = '{{Hello|Hi|Hey} {world|earth} | {Goodbye|farewell} {noobs|n3wbz|n00blets}}'

import re
re_bracks = re.compile(r'{.+?}')

# subclass list for a basic tree datatype
class bracks(list):
    def __init__(self, m):
        self.m = m

# icky procedure to create the parse tree
# I hate these but don't know how else to do it
parse_tree = []
for m in re_bracks.finditer(text):
    if not this_element:
        # this first match
        parse_tree.extend(element(m))
    else:
        # ... and all the rest
        this_element = bracks(m)
        this_start, this_end = m.span()

        # if this match is nested in the old one ...
        if this_start < previous_start and this_end > previous_end:
            # nest it inside the previous one
            previous_element.extend(this_element) 
        else:
            # otherwise make it a child of the parse_tree
            parse_tree.extend(element(m))

        previous_element = this_element
        previous_start, previous_end = this_start, this_end

Questo ti darebbe la profondità di annidamento delle espressioni tra parentesi. Aggiungi una logica simile per le pipe e saresti sulla buona strada per risolvere il problema.

Consiglio di dare un'occhiata a il motore dada per ispirazione.

Ho fatto un'implementazione di qualcosa ispirato da questo nello schema e ho sfruttato l'AST dello schema per esprimere i miei bisogni.

In particolare, sconsiglio vivamente di provare a usare una regex come parser in generale.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top