Progettazione della lingua: corrispondenza del modello 2-D


49

Questa è la Sfida quindicinale n . 6 . Tema: Language Design

C'è una chat room per questa sfida. Vieni e unisciti a noi se vuoi discutere di idee!

E ora qualcosa di completamente diverso...

Questa quindicina di giorni vogliamo sperimentare un nuovo tipo di sfida. In questa sfida, progetterai una lingua! La corrispondenza dei modelli è un problema molto comune nella programmazione e spesso molto utile per il golf del codice. Le espressioni regolari, ad esempio, possono essere utilizzate per rilevare un motivo in una riga di testo. Tuttavia, non esistono metodi stabiliti per descrivere e rilevare modelli bidimensionali.

La sfida

Devi progettare un linguaggio di corrispondenza dei motivi, che consenta la descrizione di motivi bidimensionali in blocchi di testo. La modalità di funzionamento della tua lingua sarà simile alle espressioni regolari (anche se la tua lingua non deve avere nulla in comune con regex altrimenti):

  • Come input, riceverai un blocco rettangolare di testo. Si può presumere che il testo sia composto solo da caratteri ASCII stampabili (da 0x20 a 0x7E) e da nuove righe (0x0A) per separare le righe della griglia.
  • Se una corrispondenza, in base alla descrizione del modello, può essere trovata come qualsiasi sottoinsieme di questo blocco di testo, questa corrispondenza deve essere restituita o stampata. Se la corrispondenza può essere non rettangolare, deve essere riempita in un'area rettangolare con un carattere riservato. Se sono presenti più corrispondenze valide, puoi decidere come scegliere la corrispondenza restituita (più grande, più piccola, prima, ecc.).

Per alcune applicazioni potrebbe essere utile se l'implementazione può restituire la posizione di una corrispondenza anziché la corrispondenza stessa, ma questo non è un requisito.

Per lo meno, la tua lingua dovrebbe essere in grado di abbinare un modello come una sottoregione contigua e rettangolare del suo input.

La tua risposta dovrebbe contenere:

  • Una descrizione della lingua
  • Un'implementazione di lavoro . Questo può essere un programma o un insieme di funzioni / classi in una lingua di tua scelta.
  • Dovresti dimostrare la tua lingua mostrando come può essere utilizzata per risolvere gli esempi forniti di seguito . La tua lingua non deve essere in grado di abbinarli tutti, ma devi essere in grado di abbinarne almeno 8 . Se la tua lingua può fare qualcosa di stravagante a cui non abbiamo pensato, sentiti libero di includerlo anche tu.

Se la tua risposta si basa su idee esistenti, va bene, ma per favore dai credito dove è dovuto.

estensioni

Quanto sopra descrive il minimo che deve soddisfare una richiesta valida. Tuttavia, diverse generalizzazioni potrebbero rendere ancora più utile un tale linguaggio di corrispondenza dei modelli, incluso ma non limitato a:

  • Essere in grado di ancorare il motivo a uno o più bordi, in modo da poter verificare se l'intera area di input ha un certo motivo.
  • Produrre tutte le partite invece di una sola. Puoi scegliere la semantica delle partite sovrapposte.
  • Prendendo testo non rettangolare come input.
  • Consentire ai modelli di specificare corrispondenze non rettangolari. In tal caso, l'output deve essere riempito con un rettangolo con un carattere riservato.
  • Consentire ai motivi di specificare le corrispondenze con i fori.
  • Consentire corrispondenze non contigue, come due personaggi che appaiono con un certo offset.
  • Facile specifica di rotazioni e riflessioni.
  • Opzionalmente trattare l'ingresso ciclicamente come un cilindro o un toro, in modo che i bordi opposti siano considerati adiacenti.

punteggio

L'obiettivo principale di questa sfida è quello di produrre un linguaggio di corrispondenza dei modelli 2D efficace che potrebbe essere potenzialmente utilizzato in futuro. Pertanto, un sistema di punteggio come "la lunghezza combinata più breve per risolvere gli esempi" porterebbe a codificare a fondo determinate funzionalità a spese dell'usabilità generale. Pertanto, abbiamo deciso che questa sfida è meglio lanciarsi come concorso di popolarità. Vince l'invio con il maggior numero di voti netti. Sebbene non possiamo forzare il modo in cui le persone votano, ecco alcune linee guida per ciò che gli elettori dovrebbero idealmente cercare:

  • Espressività. La lingua può risolvere una varietà di problemi, anche al di là degli esempi presentati in questa domanda? Supporta una delle estensioni suggerite?
  • Leggibilità. Quanto è intuitiva la notazione (almeno per le persone che conoscono la sintassi di base)?
  • Golfitude. Questo è ancora CodeGolf.SE. Ai fini di questo sito, sarebbe ovviamente bello avere un linguaggio corrispondente che richieda pochissimo codice per descrivere un modello.

Problemi di esempio

Il seguente frammento di stack mostra 16 problemi di esempio che un linguaggio di corrispondenza di modelli 2D potrebbe essere in grado di affrontare. Ogni esempio contiene una breve descrizione del problema e di solito è seguito da un esempio di input in cui è possibile trovare una corrispondenza e da un esempio in cui non è possibile trovare una corrispondenza (se applicabile).

Come indicato sopra, la tua lingua deve solo essere in grado di risolvere 8 di questi problemi. Tutto ciò è facoltativo, ma ovviamente dovrebbe aumentare il numero di voti che ottieni.

(No, non è necessario leggere quel codice HTML. Premi il pulsante "Esegui snippet di codice" per renderlo piacevolmente dal tuo browser, che puoi quindi visualizzare anche a schermo intero.)


Esiste un limite di tempo generale per questi problemi? Sono molto interessato a risolverlo, ma sono molto impegnato, mi ci vorranno facilmente 2 settimane per farlo.
Devon Parsons,

7
@DevonParsons Non è prevista una scadenza di iscrizione.
PhiNotPi

Sembra interessante, soprattutto da quando hai creato un nuovo tag per questo. Penso che dovrebbero esserci punti bonus per la creazione di un linguaggio 2D per questo.
mbomb007,

1
@ mbomb007 Ci sono punti bonus per la creazione di un linguaggio 2D. Sono abbastanza sicuro che otterrebbe una discreta quantità di voti. ;)
Martin Ender

@ MartinBüttner Non so nemmeno come creare una lingua, davvero. Potrebbe essere qualcosa di (semplice?) Come la creazione di un programma Python che prende un file del codice del tuo nuovo linguaggio (e lo interpreta / lo esegue in base alla sintassi definita) e produce output?
mbomb007,

Risposte:


32

SnakeEx

Risolve finora 15/16 problemi!

Interprete online ! - Specifica lingua completa - Sorgente Javascript

screenshot dell'interprete

L'idea alla base di questo linguaggio è quella di definire "serpenti" che si muovono nel testo controllando i caratteri usando una sintassi simile alla regex.

Un programma in SnakeEx è costituito da un elenco di definizioni per serpenti che utilizzano diverse sequenze di comandi. I serpenti possono generare altri serpenti usando queste definizioni, che è dove SnakeEx ottiene la maggior parte del suo potere: possiamo abbinare strutture ramificate e persino fare ricorsioni (vedi esempio di abbinamento di Paren).

Ogni programma apparirà essenzialmente come un insieme di regex, ma con l'aggiunta di comandi di direzione del modulo <dir>che cambiano la direzione del serpente e chiamano i comandi del modulo {label<dir>params}che generano più serpenti.

Per un punto di ingresso, l'interprete genera un serpente usando la prima definizione, spostandosi verso destra.

Non è terribilmente conciso, ma è molto potente e (credo) abbastanza leggibile.

aggiornamenti

  • Cambiato ! non logico e ~ per non contrassegnare le corrispondenze
  • Aggiunto <!>per risolvere colinear
  • Croci abbinati risolti
  • Risolte le scacchiere in un modo meno terribile
  • Aggiunta sintassi di chiusura limitata %{min,max}
  • Aggiunto esempio di ricorsione

soluzioni

15 risolti, 1 in corso

Puoi facilmente provare questi programmi usando l'interprete online collegato sopra!

Problema 1 - Trovare le scacchiere

m:{v<R>2}{h<>1}
v:{c<L>A1}+
h:{c<R>A2}+
c:_?(#_)+#?

Per un'introduzione dettagliata, inizia dal Problema 3.

Problema 2 - Verifica delle scacchiere

m:{v<R>2}{h<>1}
v:${c<L>A1}+$
h:${c<R>A2}+$
c:$_?(#_)+#?$

Concludere i serpenti appropriati con il simbolo di fuori limite $è un modo per far corrispondere al programma solo l'intero input.

Problema 3 - Rileva un rettangolo di cifre

m:{c<R>A1}%{2,}
c:[0-9]%{2,}

Il mserpente si sposta a destra, generando almeno 2 serpenti ( %{2,}è una chiusura che significa "2 all'infinito") usando la definizione c ( c) e spostandosi a destra ( <R>), o piuttosto verso il basso in questo caso, perché tutte le direzioni sono relative al serpente corrente. Il Aparametro è lo zucchero che specifica solo che il serpente riproduttore dovrebbe muoversi dopo la chiamata. Il 1parametro è come limitiamo le corrispondenze ai rettangoli: i parametri numerici mettono i serpenti in "gruppi". Una partita non conta se tutti i serpenti dello stesso gruppo non percorrono esattamente la stessa distanza.

Problema 4 - Trovare una parola in una ricerca di parole

m:<*>GOLF

Il comando di direzione <*>specifica che il serpente dovrebbe girare in qualsiasi direzione diagonale o ortogonale. Quindi cerca la regex semplice.

Problema 5 - Rileva input quadrati

m:{v<R>1}{h<>1}
v:${c<L>A1}+$
h:${c<R>A1}+$
c:$.+$

La chiave qui è il personaggio speciale $, che corrisponde solo se il serpente è fuori limite. Generiamo un serpente orizzontale e uno verticale; ognuno di questi genera più serpenti mentre corre lungo il bordo e tutti sono nello stesso gruppo e devono avere la stessa lunghezza.

Problema 6: trova gli alianti in un gioco di vita

m:<+>[({l1<R>A}{l2<R>A}{l3<R>})({l1<L>A}{l2<L>A}{l3<L>})]
l1:##\.
l2:[(#\.)(\.#)]#
l3:#\.\.

minizia in una delle quattro direzioni ortogonali ( <+>), raggiungendo la rotazione. Quindi, cerca le tre file a sinistra o a destra in ordine, ottenendo la riflessione.

(Nota che ho sostituito gli spazi con punti solo perché le textareas HTML usate nel mio interprete sembrano fare cose stupide se hanno più spazi di fila)

Problema 7: abbina i portali del Nether

m:{e<R>A1}{d<R>A1}%{2,22}{e<R>1}
e:~.X%{3,22}~.
d:X\.+X

Il mserpente si sposta a destra, generando un serpente per controllare il bordo sinistro, 2-22 serpenti per controllare le colonne centrali e infine un serpente per controllare il bordo destro. L' ~operatore indica che tutto ciò che segue deve essere verificato ma non deve essere contrassegnato come parte della soluzione.

Le chiusure limitate ora ci consentono di risolvere completamente e correttamente questo problema!

Problema 8 - Posizionamento del torace di Minecraft

m:~{s<>}~!{d<+>}\.
s:<+>.<BR>([$\.]<R>)%{3}
d:.<+>CC

Qui usiamo un logico not ( !), che corrisponde se e solo se il seguente token non corrisponde a nulla. La dichiarazione drileva un doppio petto in una direzione particolare, quindi !{d<+>}assicurati che non vi siano doppi forzieri in nessuna direzione ortogonale. ssi muove in un piccolo diamante attorno al quadrato corrente, verificando che almeno 3 di quegli spazi siano vuoti o fuori dal tabellone. Il finale alla \.fine corrisponde al quadrato, presupponendo che tutte le condizioni precedenti siano riuscite.

Problema 9 - Allineamento orizzontale e verticale

m:<R>?#~.*#

Il serpente mfacoltativamente gira a destra ( <R>?) prima di abbinare la sequenza. .è un carattere jolly, come in regex.

Problema 10 - Punti Colinear

m:<!>#~.*#~.*#

Con l'aggiunta della <!>direzione, possiamo risolverlo ora! <!>è come <+>ma invece di ramificarsi in quattro direzioni, si dirama in ogni direzione possibile.

Problema 12 - Evita la lettera Q

m:{h<R>A}%{4}
h:[^Qq]%{4}

Genera solo 4 serpenti che cercano quattro personaggi che non sono la lettera Q.

Problema 13 - Estrazione di diamanti

m:{tl<RB>1}{tr<RF>1}
tl:X/*{bl<L>1}X
tr:X\\*{br<R>1}X
bl:X\\*X
br:X/*X

Questo è abbastanza pulito. mgenera due serpenti, uno che va alla sua destra e uno alla sua destra in avanti. Ognuno di questi segue le barre a una X, quindi genera un altro serpente ad angolo retto rispetto alla sua direzione corrente, che segue le barre alla X inferiore.

Problema 14 - Croci corrispondenti

m:{a<R>A}+{b<R>A}+{a<R>A}+
a:{e<>P1}{c<>P2}{e<>P3}
b:{c<>P1}{c<>P2}{c<>P3}
e:\.+
c:#+

Ecco la prima volta che ho usato il Pparametro iggyback. Normalmente i serpenti sono indipendenti, ma se si effettua una chiamata con questo parametro, il serpente chiamante verrà spostato con la chiamata. Quindi e2posso controllare una sequenza di '.', Quindi una sequenza di '#', quindi un'altra sequenza di '.', E avere tutte chiamate separate in modo da poterle raggruppare con '1,' 2 'e' 3 ' , costringendo le loro lunghezze ad abbinarsi.

Problema 15 - Abbina una parola in una boggle board

m{I}:<*>p<*>a<*>n<*>a<*>m<*>a

Semplice, se prolisso. IIl parametro specifica la distinzione tra maiuscole e minuscole (possiamo specificare i parametri sulle definizioni e nelle chiamate). Il serpente gira in qualsiasi direzione, abbina il personaggio, gira di nuovo e così via.

m{EI}:<*>p<*>a<*>n<*>a<*>m<*>a

Questa è la versione senza riutilizzo dei caratteri. Il Eparametro xclusive impedisce al serpente di abbinare qualsiasi personaggio che è già stato segnato, proprio come le tracce di melma di Feersum.

Problema 16: avvolgere i bordi

m{W}:{c<R>WA}%{3}
c:###

Il Wparametro consente al serpente di avvolgere quando è fuori limite. Abbiamo anche He Vper consentire solo il confezionamento orizzontale o verticale.

Extra - Risolutore di labirinti

m{E}:$(<P>\.)+$

Risolve un labirinto ASCII in cui il pavimento percorribile è periodi. La <P>direzione significa avanti, sinistra o destra (zucchero per [<F><L><R>]).

Extra - Corrispondenza dei parentesi

m:\(~{r<>P}\)
r:[^\(\)]*(\({r<>P}\))?[^\(\)]*

Non ho ancora capito come fare Preludio, ma ecco un primo passo! Qui uso il rserpente in modo ricorsivo per abbinare la parentesi corrispondente controllando che non ci siano parentesi senza pari tra di loro.

Topologia ASCII extra : conteggio dei loop


Considereresti di aggiungere la sintassi in modo che questa lingua possa fare la sostituzione piuttosto che solo la corrispondenza? Voglio usare una soluzione a questa sfida per scrivere una voce per codegolf.stackexchange.com/questions/51231/…, ma non una singola soluzione qui trova e sostituisce. (So ​​che la mia risposta non sarebbe valida a causa delle regole di tempistica delle specifiche della lingua)
Sparr

@Sparr. Non è una cattiva idea, sarebbe sicuramente più utile per il golf del codice. Non sono sicuro di quando avrò il tempo di farlo da solo, ma lo terrò a mente.
BMac,

3
separatamente: la sintassi per i cambi di direzione è confusa. poiché il serpente avanza dopo aver abbinato un personaggio, mi sembra di dover cambiare la mia direzione di un personaggio prima di dove ha senso per me. esempio: stringa "ABC \ nDEF" e voglio abbinare il pezzo di tetris a forma di L definito da ABCF, voglio scrivere il mio serpente come "m: ABC <R> F" ma devo scrivere "m: AB <R> CF "invece. Capisco che questo corrisponde alle specifiche, ma lo trovo molto controintuitivo.
Sparr,

Ho una soluzione parziale per la sintassi del preludio. L'unico problema è che non riesco a far sì che non corrisponda se non corrisponde all'intero input: m:{a<>} a:[({n<R>A})({n<R>A}*{l<R>A}{a<>P}{r<R>A})]*{n<R>A}* l:$[^\(\)]*\([^\(\)]*$ r:$[^\(\)]*\)[^\(\)]*$ n:$[^\(\)]+$
TheNumberOne

21

Slip, Python 3.4 ( Github wiki , interprete online )

Come l'invio di Feersum, questo si basa anche sull'attraversamento della griglia, ma come l'invio di CarpetPython, questo si basa su regex. In qualche modo sembra che sia riuscito a prendere la via di mezzo.

Ci sono un sacco di funzioni non implementate che voglio aggiungere, quindi dove pertinente ho notato cosa intendo fare quando avrò tempo.


aggiornamenti

(Vedi la pagina di Github per notizie dettagliate)

  • 5 aprile 2015 : Slip ora ha un interprete online ! È ancora agli inizi, quindi potrebbero esserci alcuni bug.

  • 5 aprile 2015 : aggiornamento dell'efficienza! Ora i portali del Nether hanno un input molto più veloce (2 secondi). Inoltre sono state apportate diverse modifiche alla sintassi, quindi assicurati di controllare il wiki. Anche la numerazione dei gruppi è stata corretta.


Lo slip ha un puntatore a corrispondenza che inizia in un quadrato particolare e inizialmente è rivolto verso destra. Quando viene associato un carattere, il puntatore si sposta in avanti nella direzione corrente (sebbene l'implementazione non faccia esattamente le cose in quell'ordine).

Direzione del puntatore corrispondenza può essere impostato ad una direzione particolare ^<digit>, dove ^0, ^1, ^2, ..., ^7impostare il puntatore di N, NE, E, ..., NW rispettivamente (in senso orario).

Sono inoltre disponibili le seguenti scorciatoie:

  • ^* controlla tutte e 8 le direzioni ortogonali o diagonali,
  • ^+ controlla tutte e 4 le direzioni ortogonali.

(Piano futuro: consentire l'impostazione di direzioni arbitrarie, ad esempio (1, 2)per la mossa del cavaliere)

Ad esempio ( Problema 4 - Trovare una parola in una ricerca di parole ),

^*GOLF

prova tutte e 8 le direzioni ortogonali o diagonali, cercando la parola "GOLF". Per impostazione predefinita, Slip prova tutti i possibili quadrati di partenza e restituisce un risultato da ciascuno, filtrando i duplicati, quindi una griglia come

GOLF
O
L
FLOG

restituisce solo la riga superiore e la riga inferiore come corrispondenze (poiché la riga superiore e la colonna sinistra "GOLF" iniziano dallo stesso quadrato). Per ottenere tutte le partite, è opossibile utilizzare la modalità partita sovrapposta.

Allo stesso modo ( Problema 15 - Abbina una parola in una scheda Boggle ),

p^*a^*n^*a^*m^*a

partite panamaprovando ogni volta una direzione diversa. Usa la ibandiera per insensibilità al maiuscolo / minuscolo. Slip riutilizza i caratteri per impostazione predefinita, ma questo può essere attivato con il rflag di non ripetizione.

(Piano futuro: un modificatore della modalità di ricerca che controlla automaticamente i set di direzioni dopo ogni spostamento in modo che la ripetizione ^*non sia necessaria)

La direzione del puntatore della corrispondenza può anche essere ruotata di 90 gradi a sinistra o a destra con <o >rispettivamente. Per esempio,

 `#`#< `#<  <`#<`#

cerca il modello

  #
## 
 ##

guardando nel seguente ordine:

765
894
123

Questo ci consente di risolvere il Problema 6 - Trovare gli alianti con

^+(`#`# >`# > `#>`#> |`#`# <`# < `#<`#< | `#`#> `#>  >`#>`#| `#`#< `#<  <`#<`#)

dove le parti 1 e 2 codificano le forme dell'aliante e le parti 3 e 4 codificano le loro controparti riflesse.

Nota che Slip usa il backtick `per la fuga. Questo perché Slip ha un'altra forma di movimento che dà il nome alla lingua: il comando slip. /fa scivolare il puntatore della partita ortogonalmente a sinistra, mentre fa \scivolare il puntatore della partita ortogonalmente a destra.

Per esempio,

abc   ghi
   def

può essere abbinato da abc\def/ghi.

Sebbene non sia particolarmente utile da solo, scivolare diventa più importante quando combinato con il (?| <regex> )gruppo stazionario, che si comporta come un lookahead corrispondente. La regex interna viene abbinata, quindi alla fine di essa la posizione e la direzione del puntatore di corrispondenza vengono ripristinate allo stato precedente al gruppo stazionario.

Per esempio,

abc
def
ghi

può essere abbinato a (?|abc)\(?|def)\(?|ghi).

Allo stesso modo, Problema 12 - Evitare di risolvere la lettera Q.

%(\(?|[^qQ]{4})){4}

dove %è un comando antiscivolo per fermare l' \attivazione del primo .

Slip ha anche un'affermazione di lunghezza (?_(<group num>) <regex> ), che corrisponde alla regex all'interno solo se la sua lunghezza di corrispondenza ha la stessa lunghezza di quella del numero di gruppo dato.

Ad esempio, Problema 13 - L'estrazione di diamanti può essere facilmente risolta

^1X(`/*)X>(?_(1)`\*)X>(?_(1)`/*)X>(?_(1)`\*)

che tenta di far corrispondere prima il lato superiore sinistro del diamante, quindi afferma che gli altri tre lati hanno la stessa lunghezza.

(Esegui con vflag per output dettagliato)

Match found in rectangle: (8, 0), (12, 4)
  X
 / \
X   X
 \ /
  X

Match found in rectangle: (0, 0), (6, 6)
   X
  / \
 /   \
X     X
 \   /
  \ /
   X

Match found in rectangle: (2, 2), (4, 4)
 X
X X
 X

Match found in rectangle: (10, 2), (14, 6)
  X
 / \
X   X
 \ /
  X

Match found in rectangle: (5, 3), (9, 7)
  X
 / \
X   X
 \ /
  X

Match found in rectangle: (0, 6), (2, 8)
 X
X X
 X

Un'alternativa golfista è

^1X(`/*)(X>(?_(1)`\*)X>(?_(1)`/*)){2}

che corrisponde due volte al primo lato.

Molti degli altri problemi possono essere risolti utilizzando gli slittamenti, i gruppi fissi e le dichiarazioni di lunghezza:

Problema 14 - Croci corrispondenti:

(?|(`.+)(`#+)(`.+))(\(?|(?_(2)`.+)(?_(3)`#+)(?_(4)`.+)))*(\(?|(?_(2)`#+)(?_(3)`#+)(?_(4)`#+)))+(\(?|(?_(2)`.+)(?_(3)`#+)(?_(4)`.+)))+

Una volta catturate le larghezze delle .s e #s nella prima fila, sta scivolando fino in fondo.

Produzione:

Match found in rectangle: (0, 1), (5, 5)
.###..
######
######
.###..
.###..

Match found in rectangle: (4, 6), (6, 8)
.#.
###
.#.

Probabilmente questo può essere giocato a golf con un po 'di ricorsione, una volta che avrò risolto alcuni bug.

Problema 3 - Rileva un rettangolo di cifre:

(?|`d`d+)(\(?|(?_(1)`d+)))+

Abbina una riga superiore di due o più cifre, quindi assicurati che ogni riga in basso abbia la stessa lunghezza. `dè una classe di caratteri predefinita equivalente a [0-9].

Nota che questo trova tutte le partite .

Problema 7 - Corrispondenza dei portali inferiori:

(?|.X{2,22}.)\((?|(?_(1)X`.+X))\){3,22}(?_(1).X+.)

quali output, per l'esempio principale nella discussione originale :

Match found in rectangle: (2, 1), (5, 5)
XXXX
X..X
X..X
X..X
XXXX

Match found in rectangle: (9, 1), (14, 5)
.XXXX.
X....X
X....X
X....X
.XXXX.

Match found in rectangle: (13, 4), (17, 8)
.XXXX
X...X
X...X
X...X
.XXX.

Infine, alcune altre funzionalità di Slip includono:

  • $0, $1, $2, ..., $7ancorare il bordo nord, l'angolo nord-est, il bordo est, ecc. $+ancorano qualsiasi bordo e $*ancorano qualsiasi angolo.
  • $seguito da un carattere minuscolo imposta un punto di ancoraggio nella posizione corrente, che può essere successivamente abbinato $seguito dal corrispondente carattere maiuscolo, ad es . $ae $A.
  • # attiva / disattiva il flag di non movimento, che impedisce al puntatore della partita di spostarsi in avanti dopo la partita successiva.
  • ,corrisponde a un carattere come ., ma non lo aggiunge all'output, consentendo corrispondenze non contigue pur potendo essere riconosciuto da (?_()).

... e altro ancora. Ce ne sono davvero troppi da elencare in questa pagina.

Altri problemi

Problema 1 - Trovare le scacchiere:

(?|`#?(`_`#)+`_?)(?_(1)(?|...+))(\(?_(1)(?|`#?(`_`#)+`_?$a)))+<(?|`#?(`_`#)+`_?)(?_(9)(?|...+))(\(?_(9)(?|`#?(`_`#)+`_?)))+$A

I due problemi con la scacchiera non sono certamente il punto forte di Slip. Abbiniamo la riga superiore quindi assicuriamo che sia almeno di lunghezza 3 e si alterni tra #e _, quindi scivoliamo e abbiniamo le righe successive. Alla fine, l' $aancoraggio dovrebbe trovarsi nella parte inferiore destra della scacchiera.

Svoltiamo quindi a sinistra e ripetiamo per le colonne, assicurandoci di abbinare $Aalla fine.

Problema 2 - Verifica delle scacchiere:

$7%(\(?|`_?(`#`_)*`#?$2))+$5<%(\(?|`_?(`#`_)*`#?$0))+$3

Come il problema precedente, controlliamo che ogni riga sia corretta, quindi ruotiamo a sinistra e facciamo lo stesso per le colonne. Le ancore vengono utilizzate per assicurarsi che solo l'intera tavola sia abbinata.

Problema 9 - Allineamento orizzontale e verticale:

>?`#,*`#

Applichiamo il facoltativo? quantificare il >comando di rotazione in modo da abbinare verso destra o verso il basso. Troviamo tutti e 5 nell'esempio con la omodalità di sovrapposizione, ma solo 4 senza di esso da allora #.,##e #.,#iniziamo dalla stessa posizione.

Problema 10 - Punti collineari

^+`#(?|(,*)<(,*))(((?_(2),*)<(?_(3),*),>)+#`#){2}

Abbina a #quindi alcuni caratteri orizzontali e alcuni caratteri verticali, quindi ripeti fino al secondo #e ripeti fino al terzo #.

Problema 5 - Rilevamento di ingressi quadrati:

$7.(.*)>(?_(1).*)$3>((?|.*)\)*

Ancorare l'angolo superiore sinistro e verificare che il bordo superiore abbia la stessa lunghezza del bordo destro, prima di ancorare l'angolo inferiore destro. Se l'input passa questo, allora risaliamo all'indietro per abbinare l'intero input.

Problema 8 - Posizionamento del torace di Minecraft:

`.^+(($^|(?|`.))>){3}($^|`.|C<(($^|(?|`.))>){3})

Corri con la pbandiera per ottenere le posizioni di ogni partita. $^è un'ancora che corrisponde se la prossima mossa mettesse il puntatore della corrispondenza fuori dai limiti.

Per prima cosa abbiniamo a ., quindi controlliamo di essere circondati da tre .s / confini, quindi assicuriamo che anche la quarta casella circostante sia un ./ confine o sia una cassa singola (controllando i suoi quadrati circostanti).

Problema 11 - Verifica della sintassi Prelude :

$7>%(/(?|[^()]+$4)(?1)?|/(?|[^()]*`([^()]*$4)(?1)?/(?|[^()]*`)[^()]*$4)(?1)?)$1

Ci sono voluti alcuni tentativi, ma penso che questo sia corretto. Qui usiamo la ricorsione, che ha la stessa sintassi di PCRE.

Questo approccio richiede che l'input sia rettangolare, ma una volta che avrò fatto una corrispondenza non rettangolare tale presupposto non sarà necessario.

Ecco lo stesso approccio, giocato con più ricorsione:

$7>%((/(?|([^()]*)$4)|/(?|(?4)`((?3))(?1)?/(?|(?4)`)(?3)))*)$1

Problema 16 - Avvolgere i bordi:

%(\(?|`#{3})){3}

(Nota: il wrapping non è stato ancora inviato all'interprete online)

Ciò richiede la bandiera di avvolgimento w. Tecnicamente l'iniziale %per il non scivolamento non è necessaria, ma la partita verrebbe contata come a partire da un quadrato più in alto.

Problema EX 1 - Risolutore di labirinti:

S(^+`.)*^+E

Problema dal commento di BMac nella chat . Usa la rbandiera per nessuna modalità di ripetizione in modo che il puntatore della partita non si blocchi andando avanti e indietro.

Problema EX 2 - Riconoscimento facciale :

(?|O(,*),(?_(2),*)O)(?_(2)(\(?|,))*)\(?|,(?_(2),*)O)(?_(2)(\(?|,))*)\`\(?_(2)`_*)`_(?_(2)`_*)`/

Nota che sto solo abbinando le facce, non sto facendo nulla. Si noti che la domanda contiene simboli dell'euro, che dovranno essere sostituiti da alcuni ASCII stampabili per funzionare.


Quel modello di hash è un aliante di Conway
Heimdall,

17

PMA / Lumache (C ++)

Immagino la lingua come lumache che si muovono attorno a una griglia ed eseguono comandi. Le lumache lasciano una scia di melma su ogni quadrato su cui si spostano, che per impostazione predefinita rende successivamente il quadrato ineguagliabile. Una corrispondenza ha esito positivo se viene raggiunta la fine dell'elenco di comandi.

Ci sono abbastanza operatori ora che avremo bisogno di un elenco di precedenza per tenerne traccia. Le operazioni vengono risolte nel seguente ordine:

  1. All'interno dei gruppi ( ) [ ]
  2. Dividi lungo il carattere di alternanza |
  3. Valuta tutto a sinistra di `un gruppo
  4. Quantificatori [m],[n]en
  5. asserzioni = !
  6. Concatenazione

L'interprete è scritto in C ++. Il codice sorgente abissale può essere trovato qui .

Problema 4: Ricerca per parola

Il programma

z\G\O\L\F

è sufficiente per ottenere un valore di verità o falsità per stabilire se la parola viene trovata. z(uno dei 15 comandi direzionali assoluti o relativi) corrisponde in qualsiasi direzione ottinearia. Più comandi di direzione consecutivi sono OR insieme. Ad esempio ruldysarebbe quasi equivalente a z, poiché quelli sono i comandi per destra, su, sinistra, giù e qualsiasi direzione diagonale. Tuttavia, le direzioni verrebbero testate in un ordine diverso. Il primo carattere abbinato è sempre quello su cui inizia la lumaca, indipendentemente dalla direzione. \<character> corrisponde a un singolo carattere letterale.

La strategia di default è provare il modello in ogni quadrato nel riquadro di delimitazione dell'input giustificato a sinistra e produrre il numero di corrispondenze. Se è necessario un valore booleano 1o, 0è possibile utilizzare il seguente programma:

?
z\G\O\L\F

Se nel file di origine è presente almeno una nuova riga, la prima riga viene considerata come un'opzione per la conversione iniziale dell'input in una forma rettangolare. L' ?opzione stampa 0 o 1 a seconda che ci sia una corrispondenza ovunque.

Problema 15: Boggle

Dopo aver implementato l'alternanza, è ora possibile risolvere Boggle. Sarebbe meglio usare la corrispondenza senza distinzione tra maiuscole e minuscole per questo, ma l'implementazione non è la mia massima priorità.

\p|\P)z(\a|\A)z{\n|\N)z{\a|\A}z(\m|\M)z(\a|\A

|funziona esattamente come una regex monodimensionale. Esistono due coppie corrispondenti di delimitatori per il raggruppamento, vale a dire ()e {}. Una parentesi chiusa chiuderà tutti i gruppi aperti del tipo opposto che si frappongono tra esso e il più vicino dello stesso tipo. Ad esempio, di seguito {({{), rimane aperto solo il gruppo più a sinistra. Come puoi vedere, i simboli di raggruppamento ineguagliati ai bordi sono implicitamente chiusi. C'è un altro comando di raggruppamento in `cui non entrerò ora.

Problema 8: Bauli di Minecraft

Il programma stampa il numero di posizionamenti petto validi. Questo è stato il primo che ho dovuto giocare a golf e ha ridotto il numero di byte (17) alcune volte.

\.!(o\C)2o(!\Cw)3
  • \. corrisponde letteralmente a un punto, nel punto iniziale della partita.
  • !(o\C)2equivale a !((o\C)2)poiché la quantificazione ha una precedenza più elevata dell'asserzione. <atom> <number>significa ripetere <atom>esattamente i <number>tempi. ogira la lumaca in qualsiasi direzione ortogonale. !è un'affermazione negativa. Pertanto questa parte verifica l'assenza di un doppio petto adiacente.
  • o gira in una direzione ortogonale.
    • (!\Cw)3afferma che non c'è Cdavanti alla chiocciola, quindi gira 3 volte in senso antiorario.

Problema 2: verifica delle scacchiere

&
\#!(o^_)|\_!(o^#

L' &opzione imposta l'output del programma come 1se la corrispondenza avesse successo in tutte le posizioni, e in caso 0contrario. ^ccorrisponde a un personaggio che non lo è c, equivalentemente a [^c]regex. Nel complesso, il programma significa: Stampa 1 se in ogni posizione nel rettangolo di delimitazione dell'input, c'è o #che non è ortogonalmente adiacente a un carattere che non lo è _, o _che non è ortogonalmente adiacente a un personaggio che è no #; altrimenti 0.


L'idea della scia di melma è buona per affrontare il boggle, stavo avendo dei problemi con questo
BMac

Questa è una buona soluzione per il problema Boggle. Non posso risolverlo con il mio approccio.
Logic Knight

14

La classe Re2d, Python 2

Aggiornamento: aggiunto il problema "9. Allineamento".

Il mio approccio è quello di utilizzare il modulo re Python per fare la ricerca e la corrispondenza. La classe Re2d prepara il testo per l'elaborazione, esegue le funzioni re e formatta i risultati per l'output.

Si noti che questo non è un linguaggio completamente nuovo: è il linguaggio delle espressioni regolari standard proiettato in 2 dimensioni con flag aggiunti per ulteriori modalità 2D.

La classe ha il seguente utilizzo:

re2dobject = Re2d(<horizontal pattern>, [<vertical pattern>], [<flags>])

Entrambi i modelli sono modelli di testo lineare standard RE. Se non viene fornito un motivo verticale, la classe utilizzerà anche il motivo orizzontale per la corrispondenza verticale. I flag sono i flag RE standard con alcune estensioni 2D.

analisi

1. Finding chessboards
Chessboard pattern at (2, 1, 4, 3)

print '\n1. Finding chessboards'
reob1 = Re2d('#(_#)+_?|_(#_)+#?')
found = reob1.search('~______~\n~##_#_#~\n~#_#_##~\n~##_#_#~\n~______~')
print 'Chessboard pattern at', found
assert not reob1.search('#_##\n_#_#\n__#_\n#_#_\n#_#_')

Il metodo di ricerca ha trovato un modello a scacchiera e restituisce una posizione a 4 tuple. La tupla ha la x,yposizione del primo personaggio dell'incontro e width, heightl'area dell'area abbinata. Viene fornito un solo motivo, quindi verrà utilizzato per la corrispondenza orizzontale e verticale.

2. Verifying chessboards
Is chess? True

print '\n2. Verifying chessboards'
reob2 = Re2d('^#(_#)*_?|_(#_)*#?$')
print 'Is chess?', reob2.match('_#_#_#_#\n#_#_#_#_\n_#_#_#_#')
assert not reob2.match('_#_#_#__\n__#_#_#_\n_#_#_#__')

La scacchiera è stata verificata con il metodo match che restituisce un valore booleano. Si noti che i caratteri ^e $inizio e fine sono richiesti per abbinare l' intero testo.

3. Rectangle of digits
Found: [(0, 1, 5, 3), (1, 1, 4, 3), (2, 1, 3, 3), (3, 1, 2, 3), (0, 2, 5, 2), (1, 2, 4, 2), (2, 2, 3, 2), (3, 2, 2, 2), (6, 3, 2, 2)]
Not found: None

print '\n3. Rectangle of digits'
reob3 = Re2d(r'\d\d+', flags=MULTIFIND)
print 'Found:', reob3.search('hbrewvgr\n18774gwe\n84502vgv\n19844f22\ncrfegc77')
print 'Not found:', reob3.search('uv88wn000\nvgr88vg0w\nv888wrvg7\nvvg88wv77')

Ora usiamo la MULTIFINDbandiera per restituire tutte le possibili corrispondenze per il blocco di 2+ cifre. Il metodo trova 9 possibili corrispondenze. Si noti che possono sovrapporsi.

4. Word search (orthogonal only)
Words: [(0, 0, 4, 1), (0, 3, 4, 1), (3, 3, -4, -1), (3, 2, -4, -1), (3, 0, -4, -1)] [(0, 0, 1, 4), (3, 0, 1, 4), (3, 3, -1, -4), (0, 3, -1, -4)]
Words: ['SNUG', 'WOLF', 'FLOW', 'LORE', 'GUNS'] ['S\nT\nE\nW', 'G\nO\nL\nF', 'F\nL\nO\nG', 'W\nE\nT\nS']
No words: [] []

print '\n4. Word search (orthogonal only)'
words = 'GOLF|GUNS|WOLF|FLOW|LORE|WETS|STEW|FLOG|SNUG'
flags = HORFLIP | VERFLIP | MULTIFIND
reob4a, reob4b = Re2d(words, '.', flags), Re2d('.', words, flags)
matching = 'SNUG\nTEQO\nEROL\nWOLF'
nomatch = 'ABCD\nEFGH\nIJKL\nMNOP'
print 'Words:', reob4a.search(matching), reob4b.search(matching)
print 'Words:', reob4a.findall(matching), reob4b.findall(matching)
print 'No words:', reob4a.findall(nomatch), reob4b.findall(nomatch)

Questo test mostra l'uso della rotazione verticale e orizzontale. Ciò consente di abbinare le parole invertite. Le parole diagonali non sono supportate. La MULTIFINDbandiera consente più partite sovrapposte in tutte e 4 le direzioni. Il metodo findall utilizza la ricerca per trovare le caselle corrispondenti, quindi estrae i blocchi corrispondenti di testo. Nota come la ricerca utilizza larghezza e / o altezza negative per le corrispondenze nella direzione opposta. Le parole in direzione verticale hanno nuovi caratteri di linea - questo è coerente con il concetto di blocchi di caratteri 2D.

7. Calvins portals
Portals found: [(3, 1, 5, 6)]
Portal not found None

print '\n7. Calvins portals'
reob7 = Re2d(r'X\.{2,22}X|.X{2,22}.', r'X\.{3,22}X|.X{3,22}.', MULTIFIND)
yes = '....X......\n.XXXXXX.XX.\n...X...X...\n.X.X...XXX.\n...X...X.X.\n.XXX...X.X.\nX..XXXXX.X.'
no = 'XX..XXXX\nXX..X..X\nXX..X..X\n..X.X..X\n.X..X.XX'
print 'Portals found:', reob7.search(yes)
print 'Portal not found', reob7.search(no)

Questa ricerca aveva bisogno di schemi separati per ogni dimensione poiché la dimensione minima è diversa per ciascuna.

9. Alignment
Found: ['#.,##', '##'] ['#\n.\n,\n.\n#', '#\n,\n.\n#']
Found: [(3, 4, 5, 1), (6, 4, 2, 1)] [(7, 0, 1, 5), (3, 1, 1, 4)]
Not found: None None

print '\n9. Alignment'
reob9a = Re2d(r'#.*#', r'.', MULTIFIND)
reob9b = Re2d(r'.', r'#.*#', MULTIFIND)
matching = '.,.,.,.#.,\n,.,#,.,.,.\n.,.,.,.,.,\n,.,.,.,.,.\n.,.#.,##.,\n,.,.,.,.,.'
nomatch = '.,.#.,.,\n,.,.,.#.\n.,#,.,.,\n,.,.,.,#\n.#.,.,.,\n,.,.#.,.\n#,.,.,.,\n,.,.,#,.'
print 'Found:', reob9a.findall(matching), reob9b.findall(matching)
print 'Found:', reob9a.search(matching), reob9b.search(matching)
print 'Not found:', reob9a.search(nomatch), reob9b.search(nomatch)

Questo set di 2 ricerche trova 2 corrispondenze verticali e 2 orizzontali, ma non riesce a trovare la #.,#stringa incorporata .

10. Collinear Points (orthogonal only)
Found: [(0, 1, 7, 1)] [(3, 1, 1, 4)]
Not found: None None

print '\n10. Collinear Points (orthogonal only)'
matching = '........\n#..#..#.\n...#....\n#.......\n...#....'
nomatch = '.#..#\n#..#.\n#....\n..#.#'
reob10h = Re2d(r'#.*#.*#', '.')
reob10v = Re2d('.', r'#.*#.*#')
flags = MULTIFIND
print 'Found:', reob10h.search(matching, flags), reob10v.search(matching, flags)
print 'Not found:', reob10h.search(nomatch, flags), reob10v.search(nomatch, flags)

Qui utilizziamo 2 ricerche per trovare corrispondenze in entrambe le direzioni. È in grado di trovare più corrispondenze ortogonali ma questo approccio non supporta le corrispondenze diagonali.

12. Avoid qQ
Found: (2, 2, 4, 4)
Not found: None

print '\n12. Avoid qQ'
reob12 = Re2d('[^qQ]{4,4}')
print 'Found:', reob12.search('bhtklkwt\nqlwQklqw\nvtvlwktv\nkQtwkvkl\nvtwlkvQk\nvnvevwvx')
print 'Not found:', reob12.search('zxvcmn\nxcvncn\nmnQxcv\nxcvmnx\nazvmne')

Questa ricerca trova la prima corrispondenza.

13. Diamond Mining
.X.
X.X
.X.

.X.
X.X
.X.

..X..
./.\.
X...X
.\./.
\.X..

..X..
./.\.
X...X
.\./.
..X..

.XX.\
//.\.
X...X
.\./.
..X..

...X...
../.\..
./.X.\.
X.X.X.X
.\.X.//
..\./X.
.X.X..\

Diamonds: [(2, 2, 3, 3), (0, 6, 3, 3)] [(8, 0, 5, 5), (10, 2, 5, 5), (5, 3, 5, 5)] [(0, 0, 7, 7)]
Not found: None None None

print '\n13. Diamond Mining'
reob13a = Re2d(r'.X.|X.X', flags=MULTIFIND)
reob13b = Re2d(r'..X..|./.\\.|X...X|.\\./.', flags=MULTIFIND)
reob13c = Re2d(r'...X...|../.\\..|./...\\.|X.....X|.\\.../.|..\\./..', flags=MULTIFIND)
match = '''
...X......X....
../.\..../.\...
./.X.\..X...X..
X.X.X.XX.\./.\.
.\.X.//.\.X...X
..\./X...X.\./.
.X.X..\./...X..
X.X....X.......
.X.............
'''.strip().replace(' ', '')
nomatch = '''
.X......./....
.\....X.......
...X.\.\...X..
..X.\...\.X.\.
...X.X...X.\.X
../X\...\...X.
.X...\.\..X...
..\./.X....X..
...X..../.....
'''.strip().replace(' ', '')
for diamond in reob13a.findall(match)+reob13b.findall(match)+reob13c.findall(match):
    print diamond+'\n'
print 'Diamonds:', reob13a.search(match), reob13b.search(match), reob13c.search(match)
print 'Not found:', reob13a.search(nomatch), reob13b.search(nomatch), reob13c.search(nomatch)

Il problema del diamante è più difficile. Sono necessari tre oggetti di ricerca per le tre dimensioni. Può trovare i sei diamanti nel set di test, ma non si adatta a diamanti di dimensioni variabili. Questa è solo una soluzione parziale al problema del diamante.

Codice Python 2

import sys
import re

DEBUG = re.DEBUG
IGNORECASE = re.IGNORECASE
LOCALE = re.LOCALE
UNICODE = re.UNICODE
VERBOSE = re.VERBOSE
MULTIFIND = 1<<11
ROTATED = 1<<12     # not implemented
HORFLIP = 1<<13
VERFLIP = 1<<14
WRAPAROUND = 1<<15  # not implemented

class Re2d(object):
    def __init__(self, horpattern, verpattern=None, flags=0):
        self.horpattern = horpattern
        self.verpattern = verpattern if verpattern != None else horpattern
        self.flags = flags

    def checkblock(self, block, flags):
        'Return a position if block matches H and V patterns'
        length = []
        for y in range(len(block)):
            match = re.match(self.horpattern, block[y], flags)
            if match:
                length.append(len(match.group(0)))
            else:
                break
        if not length:
            return None
        width = min(length)
        height = len(length)
        length = []
        for x in range(width):
            column = ''.join(row[x] for row in block[:height])
            match = re.match(self.verpattern, column, flags)
            if match:
                matchlen = len(match.group(0))
                length.append(matchlen)
            else:
                break
        if not length:
            return None
        height = min(length)
        width = len(length)
        # if smaller, verify with RECURSIVE checkblock call:
        if height != len(block) or width != len(block[0]):
            newblock = [row[:width] for row in block[:height]]
            newsize = self.checkblock(newblock, flags)
            return newsize
        return width, height

    def mkviews(self, text, flags):
        'Return views of text block from flip/rotate flags, inc inverse f()'
        # TODO add ROTATED to generate more views
        width = len(text[0])
        height = len(text)
        views = [(text, lambda x,y,w,h: (x,y,w,h))]
        if flags & HORFLIP and flags & VERFLIP:
            flip2text = [row[::-1] for row in text[::-1]]
            flip2func = lambda x,y,w,h: (width-1-x, height-1-y, -w, -h)
            views.append( (flip2text, flip2func) )
        elif flags & HORFLIP:
            hortext = [row[::-1] for row in text]
            horfunc = lambda x,y,w,h: (width-1-x, y, -w, h)
            views.append( (hortext, horfunc) )
        elif flags & VERFLIP:
            vertext = text[::-1]
            verfunc = lambda x,y,w,h: (x, height-1-y, w, -h)
            views.append( (vertext, verfunc) )
        return views

    def searchview(self, textview, flags=0):
        'Return matching textview positions or None'
        result = []
        for y in range(len(textview)):
            testtext = textview[y:]
            for x in range(len(testtext[0])):
                size = self.checkblock([row[x:] for row in testtext], flags)
                if size:
                    found = (x, y, size[0], size[1])
                    if flags & MULTIFIND:
                        result.append(found)
                    else:
                        return found
        return result if result else None

    def search(self, text, flags=0):
        'Return matching text positions or None'
        flags = self.flags | flags
        text = text.split('\n') if type(text) == str else text
        result = []
        for textview, invview in self.mkviews(text, flags):
            found = self.searchview(textview, flags)
            if found:
                if flags & MULTIFIND:
                    result.extend(invview(*f) for f in found)
                else:
                    return invview(*found)
        return result if result else None

    def findall(self, text, flags=0):
        'Return matching text blocks or None'
        flags = self.flags | flags
        strmode = (type(text) == str)
        text = text.split('\n') if type(text) == str else text
        result = []
        positions = self.search(text, flags)
        if not positions:
            return [] if flags & MULTIFIND else None
        if not flags & MULTIFIND:
            positions = [positions]
        for x0,y0,w,h in positions:
            if y0+h >= 0:
                lines = text[y0 : y0+h : cmp(h,0)]
            else:
                lines = text[y0 : : cmp(h,0)]
            if x0+w >= 0:
                block = [row[x0 : x0+w : cmp(w,0)] for row in lines]
            else:
                block = [row[x0 : : cmp(w,0)] for row in lines]
            result.append(block)
        if strmode:
            result = ['\n'.join(rows) for rows in result]
        if flags & MULTIFIND:
            return result
        else:
            return result[0]

    def match(self, text, flags=0):
        'Return True if whole text matches the patterns'
        flags = self.flags | flags
        text = text.split('\n') if type(text) == str else text
        for textview, invview in self.mkviews(text, flags):
            size = self.checkblock(textview, flags)
            if size:
                return True
        return False

Se il problema con il diamante non era chiaro, i diamanti possono avere qualsiasi dimensione, non solo 0, 1 o 2. Modifica: ho modificato le specifiche per renderlo più chiaro.
PhiNotPi

Fatto. Prenderò nota nella risposta che Re2d ha solo una soluzione parziale a questo problema. Non può ridimensionare a dimensioni variabili. È ok?
Logic Knight,

Si va bene.
PhiNotPi

14

Grime , Haskell

introduzione

Grime si basa su grammatiche booleane . L'idea di base è costruire modelli rettangolari da componenti più piccoli e verificare se si trovano nella matrice di input. Finora, Grime supporta solo corrispondenze rettangolari e risolve almeno 11 problemi in modo più o meno elegante.

EDIT: risolto il problema delle croci (grazie a DLosc per aver individuato il bug) e aggiunto l'estrazione di diamanti.

EDIT2: aggiunte classi di personaggi, ispirate a quelle di Slip. Inoltre, è stata modificata la sintassi dei flag delle opzioni, migliorato il parser delle espressioni e aggiunto il problema no-Q.

EDIT3: implementato vincoli di dimensione e aggiunto il problema dei portali Nether.

uso

Un programma Grime è chiamato grammatica e l'estensione di file corretta per una grammatica è .gr, sebbene non sia imposta. La grammatica viene valutata come

runhaskell grime.hs [options] grammarfile matrixfile

dove si matrixfiletrova un file contenente la matrice di caratteri. Ad esempio, la grammatica delle cifre verrebbe valutata come

runhaskell grime.hs digits.gr digit-matrix

Per una maggiore velocità, ti consiglio di compilare il file con ottimizzazioni:

ghc -O2 grime.hs
./grime digits.gr digit-matrix

Per impostazione predefinita, l'interprete stampa la prima corrispondenza trovata, ma questa può essere controllata utilizzando i flag di opzione:

  • -e: abbina solo l'intera matrice, stampa 1per corrispondenza e 0per nessuna corrispondenza.
  • -n: stampa il numero di corrispondenze o l'intera matrice, se -epresente.
  • -a: stampa tutte le partite.
  • -p: stampa anche le posizioni delle partite, nel formato (x,y,w,h).
  • -s: non stampa gli abbinamenti stessi.
  • -d: stampa informazioni di debug.

Le opzioni possono anche essere specificate nella grammatica, inserendole prima di qualsiasi riga e aggiungendo una virgola ,(vedi sotto per gli esempi).

Sintassi e semantica

Una grammatica Grime è costituita da una o più definizioni , ciascuna su una riga separata. Ciascuno di essi definisce il valore di un non terminale e uno di essi deve definire il livello superiore anonimo non finale . La sintassi di una definizione è o N=Eo E, dove Nè una lettera maiuscola ed Eè un'espressione .

Le espressioni sono costruite come segue.

  • Qualsiasi carattere con cui è stata eseguita la escape \corrisponde a qualsiasi 1x1rettangolo contenente quel carattere.
  • . corrisponde a qualsiasi singolo personaggio.
  • $corrisponde a un 1x1rettangolo esterno alla matrice dei caratteri.
  • _ corrisponde a qualsiasi rettangolo di larghezza o altezza zero.
  • I gruppi di caratteri predefiniti sono digit, uppercase, lowercase, alphabetic, alpha numeric e symbol.
  • Le nuove classi di caratteri possono essere definite dalla sintassi [a-prt-w,d-gu]. Le lettere a sinistra sono incluse e quelle a destra sono escluse, quindi questo corrisponde esattamente alle lettere abchijklmnoprtvw. Se il lato sinistro è vuoto, viene utilizzato per contenere tutti i caratteri. La virgola può essere omessa se il lato destro è vuoto. I personaggi [],-\devono essere sfuggiti a \.
  • Una lettera maiuscola senza caratteri di escape non è un terminale e corrisponde all'espressione che gli è stata assegnata.
  • Se Pe Qsono espressioni, allora PQè solo la loro concatenazione orizzontale, ed P/Qè la loro concatenazione verticale, con Pin cima.
  • P+è una o più Ps allineate orizzontalmente ed P/+è la stessa allineata verticalmente.
  • Le operazioni booleane sono indicate P|Q, P&Qe P!.
  • P?è una scorciatoia per P|_, P*per P+|_e P/*per P/+|_.
  • P#corrisponde a qualsiasi rettangolo che contiene una corrispondenza di P.
  • P{a-b,c-d}, Dove abcdsono interi non negativi, è un vincolo di dimensione su P. Se Pè una classe di caratteri, allora l'espressione corrisponde a qualsiasi mxnrettangolo contenente solo quei caratteri, purché mcompreso tra ae bcompreso, compreso ntra ce dcompreso. In altri casi, l'espressione corrisponde a qualsiasi rettangolo che abbia la dimensione corretta e che Pcorrisponda anche. Se ao csono omessi, sono considerati come 0, e se bo dsono omessi, sono infiniti. Se il trattino tra ae bviene omesso, utilizziamo lo stesso numero per entrambe le estremità dell'intervallo. Se l'interoc-dla parte viene omessa, entrambi gli assi sono vincolati. Per chiarire, {-b}è equivalente a {0-b,0-b}ed {a-,c}è equivalente a {a-infinity,c-c}.

Appunti

Grime consente definizioni paradossali come A=A!con un comportamento indefinito. Tuttavia, non causeranno arresti anomali o loop infiniti.

Grime supporta input non rettangolari; le righe sono semplicemente allineate a sinistra e gli spazi possono essere abbinati usando $.

In futuro, vorrei implementare quanto segue:

  • Abbinamento più veloce. Attualmente, l'interprete non tiene conto del fatto che, ad esempio, .può corrispondere solo ai 1x1rettangoli. Fino a quando non trova una corrispondenza, prova tutti i rettangoli di tutte le dimensioni in ordine, fallendo immediatamente per ciascuno di essi.
  • Operazioni di rotazione e riflessione, per la ricerca di parole e sfide dell'aliante.
  • Partite non rettangolari che utilizzano contesti , il che sarebbe utile nella sfida del tabellone Boggle. Ad esempio, Pv(Q>R)significa Pcon il contesto inferiore ( Qcon il contesto giusto R). Si abbinerebbe ai motivi a L

    PPP
    PPP
    QQQRRRR
    QQQRRRR
    QQQRRRR
    

I compiti

Dato approssimativamente in ordine di complessità.

Rettangolo di cifre

d{2-}

Questo è semplice: almeno un rettangolo di cifre 2x2.

No q o Q

[,qQ]{4}

Questo è quasi semplice come il primo; ora abbiamo una dimensione più limitata e una classe di caratteri personalizzata.

Allineamento orizzontale e verticale

\#.*\#|\#/./*/\#

Ora abbiamo alcuni personaggi fuggiti. Fondamentalmente, questo corrisponde a uno #, quindi a qualsiasi numero di qualsiasi carattere, quindi #, sia in orizzontale che in verticale.

Rileva input quadrati

S=.|S./+/.+
e,S

Questa grammatica è molto semplice, in sostanza definisce che un quadrato è o un 1x1rettangolo o un quadrato più piccolo con una colonna incollata sul bordo destro e una riga attaccata sul fondo. Nota anche l' eopzione prima del livello superiore non terminale, che attiva / disattiva la verifica dell'intero input.

Trovare una parola in una ricerca per parola

G=\G
O=\O
L=\L
F=\F
GOLF|FLOG|G/O/L/F|F/L/O/G|G.../.O../..L./...F|...G/..O./.L../F...|F.../.L../..O./...G|...F/..L./.O../G...

Questo è orribile, dal momento che Grime non ha operazioni di rotazione o riflessione. È anche estremamente lento, poiché Grime non sa che le partite possono essere solo di dimensioni 4x1, 1x4o 4x4.

Il problema dell'aliante potrebbe essere risolto in modo simile, ma sono troppo pigro per scriverlo.

Portali del Nether

.\X+./\X/+\.{2-22,3-22}\X/+/.\X+.

Con l'operatore di restrizione delle dimensioni, questo non è così difficile. La parte centrale \.{2-22,3-22}corrisponde a qualsiasi rettangolo delle .dimensioni corrette, quindi aggiungiamo solo colonne di Xs su entrambi i lati e viriamo a righe di Xs con estremità ignorate nella parte superiore e inferiore di quella.

Croci corrispondenti

E=\.+/+
F=\#+/+
EFE/F/EFE&(E/F/E)F(E/F/E)

Ciò che abbiamo qui è una congiunzione (AND logico) di due espressioni. Il nonterminale Ecorrisponde a un rettangolo non vuoto di .s e Fun rettangolo non vuoto di #s. Il lato sinistro della congiunzione corrisponde a rettangoli del tipo

...####..
...####..
...####..
#########
#########
.....##..
.....##..

dove abbiamo EFEin cima, quindi F, e poi di EFEnuovo. Il lato destro corrisponde alle trasposizioni di questi, quindi otteniamo esattamente le croci.

Estrazione di diamanti

C=./+
T=\X|CTC/\/.+\\
B=\X|\\.+\//CBC
CTC/\X.+\X/CBC

Il nonterminal Cè una scorciatoia per qualsiasi 1xncolonna. La metà superiore di un diamante è abbinata a T: è una singola Xo un'altra Tcircondata da una colonna su entrambi i lati e una fila /[something]\sotto di essa. Bcorrisponde alla parte inferiore di un diamante allo stesso modo e il livello superiore non terminale è solo una riga della forma X[something]Xtra una metà superiore e una metà inferiore.

Trovare le scacchiere

(\#\#|\#/\#|\_\_|\_/\_)#!&[#_]{3-}

Il lato destro [#_]{3-}corrisponde a uno 3x3o più rettangoli di #s o _s, mentre il lato sinistro garantisce che non contenga due #s o _s adiacenti .

Verifica delle scacchiere

e,(\#\#|\#/\#|\_\_|\_/\_)#!&[#_]+/+

Questo è fondamentalmente lo stesso di sopra, tranne per il fatto che possiamo abbinare qualsiasi rettangolo non vuoto, ma è necessario utilizzare il eflag per l'intera verifica dell'input.

Verifica la sintassi Prelude

A=[,()]/*
P=A*|P(A/\(/A)P(A/\)/A)P
e,P

Questa è probabilmente la grammatica più interessante finora. Il nonterminale Acorrisponde a qualsiasi colonna che non contiene (o )e Pcorrisponde a un numero di As, o due parentesi corrispondenti, tra e all'esterno delle quali sono presenti più Ps.


@DLosc Risolto, grazie per aver trovato il bug!
Zgarb,

Funzionerebbe \#(.*|./*)\#?
Seequ

@Sieg Per l'allineamento? Sfortunatamente no, perché sarebbe analizzato come "uno #a sinistra, quindi una riga o una colonna di qualcosa, quindi uno #a destra". Devo specificare che gli #s sono concatenati verticalmente alla colonna usando le barre /.
Zgarb,

10

TMARL

Linguaggio di corrispondenza e riconoscimento dei modelli

Descrizione

Il mio interprete occupa caratteri 24K (i frammenti di codice occupano caratteri? ), Quindi la descrizione completa è disponibile qui .

La parte migliore: l'interprete è in Javascript, il che significa che puoi provarlo proprio qui!

E per i problemi:

# 1 - Trovare scacchiere

$#_#
a
$_#_
bvacvbS5&(avcS5)G0G2P

&aggiunge ricerche. Il G2 alla fine ottiene il 3 ° elemento in un elemento di corrispondenza, la corrispondenza effettiva. I primi 2 elementi sono coordinate xey (1 basato, non 0).

# 3 - Rileva un rettangolo di cifre

$DD
$DD
S1G2P

Penso che questo sia piuttosto semplice.

# 4 - Trovare una parola in una ricerca di parole

$GO\LF
a
$G
$*O
$**\L
$***F
S6&(aS6)G0G2P

L' Sargomento è anche in modo che cercherà tutte le rotazioni. È maggiore di 4 perché può quindi essere aggiunto alla ricerca successiva (non è possibile aggiungere singole corrispondenze).

# 5 - Rileva input quadrati

IL-(IG0L)!P

Non sono sicuro che ciò sia completamente legale, poiché determina correttamente la quadratura solo se l'input è un rettangolo. Confronta la lunghezza dell'input ILcon la lunghezza della prima riga IG0Le la inverte.

# 6 - Trova gli alianti in un gioco di vita

$## 
$# #
$# 
a
$ ##
$## 
$  #
bS6&(bMS6)&(aS6)&(aMS6)G0G2P

Finalmente un uso per lo specchio!

# 12 - Evita la lettera Q

$[^Qq]
~4*4S1G2P

S1 perché è necessaria solo 1 corrispondenza.

Farò alcuni dei più difficili dopo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.