2009-06-17 29 views
6

Qual è il modo migliore per utilizzare le espressioni regolari con le opzioni (flag) in Haskellcase-insensitive espressioni regolari

io uso

Text.Regex.PCRE 

La documentazione elenca alcune opzioni interessanti come compCaseless, compUTF8, .. Ma non so come usarli con (= ~)

risposta

16

Tutti i moduli Text.Regex.* fanno pesante uso di typeclasses, che sono lì per l'estensibilità e il comportamento "sovraccarico", ma rendere l'uso meno obvio noi dal vedere solo i tipi.

Ora, probabilmente è stato avviato dal dispositivo di base =~.

(=~) :: 
    (RegexMaker Regex CompOption ExecOption source 
    , RegexContext Regex source1 target) 
    => source1 -> source -> target 
(=~~) :: 
    (RegexMaker Regex CompOption ExecOption source 
    , RegexContext Regex source1 target, Monad m) 
    => source1 -> source -> m target 

Per utilizzare =~, deve esistere un'istanza RegexMaker ... per LHS e RegexContext ... per RHS e risultato.

class RegexOptions regex compOpt execOpt | ... 
     | regex -> compOpt execOpt 
     , compOpt -> regex execOpt 
     , execOpt -> regex compOpt 
class RegexOptions regex compOpt execOpt 
     => RegexMaker regex compOpt execOpt source 
     | regex -> compOpt execOpt 
     , compOpt -> regex execOpt 
     , execOpt -> regex compOpt 
    where 
    makeRegex :: source -> regex 
    makeRegexOpts :: compOpt -> execOpt -> source -> regex 

Un esempio valido di tutte queste classi (ad esempio, regex=Regex, compOpt=CompOption, execOpt=ExecOption e source=String) significa che è possibile compilare un regex con compOpt,execOpt mangiare da qualche forma source. (Inoltre, dato qualche regex tipo, v'è esattamente un compOpt,execOpt set che va con esso. Lotti di diversi source tipi sono okay, però.)

class Extract source 
class Extract source 
     => RegexLike regex source 
class RegexLike regex source 
     => RegexContext regex source target 
    where 
    match :: regex -> source -> target 
    matchM :: Monad m => regex -> source -> m target 

Un esempio valido di tutte queste classi (ad esempio, regex=Regex , source=String, target=Bool) significa che è possibile abbinare uno source e uno regex per produrre uno target. (Altri validi target s Date queste specifiche regex e source sono Int, MatchResult String, MatchArray, etc.)

mettere questi insieme ed è abbastanza evidente che =~ e =~~ sono semplicemente funzioni comfort

source1 =~ source 
    = match (makeRegex source) source1 
source1 =~~ source 
    = matchM (makeRegex source) source1 

e anche che =~ e =~~ non lasciare spazio per passare varie opzioni a makeRegexOpts.

Si potrebbe rendere il proprio

(=~+) :: 
    (RegexMaker regex compOpt execOpt source 
    , RegexContext regex source1 target) 
    => source1 -> (source, compOpt, execOpt) -> target 
source1 =~+ (source, compOpt, execOpt) 
    = match (makeRegexOpts compOpt execOpt source) source1 
(=~~+) :: 
    (RegexMaker regex compOpt execOpt source 
    , RegexContext regex source1 target, Monad m) 
    => source1 -> (source, compOpt, execOpt) -> m target 
source1 =~~+ (source, compOpt, execOpt) 
    = matchM (makeRegexOpts compOpt execOpt source) source1 

che potrebbe essere utilizzato come

"string" =~+ ("regex", CompCaseless + compUTF8, execBlank) :: Bool 

o sovrascrivere =~ e =~~ con metodi che possono accettare le opzioni

import Text.Regex.PCRE hiding ((=~), (=~~)) 

class RegexSourceLike regex source 
    where 
    makeRegexWith source :: source -> regex 
instance RegexMaker regex compOpt execOpt source 
     => RegexSourceLike regex source 
    where 
    makeRegexWith = makeRegex 
instance RegexMaker regex compOpt execOpt source 
     => RegexSourceLike regex (source, compOpt, execOpt) 
    where 
    makeRegexWith (source, compOpt, execOpt) 
     = makeRegexOpts compOpt execOpt source 

source1 =~ source 
    = match (makeRegexWith source) source1 
source1 =~~ source 
    = matchM (makeRegexWith source) source1 

o si può solo utilizzare match, makeRegexOpts, ecc. Direttamente dove necessario.

+0

Ah, sembra che sia stato battuto per la soluzione. Questo è quello che ottengo per scrivere ogni sorta di cose inutili: -/ – ephemient

+0

Ah, mi sento un po 'in colpa ora, il tuo offre sicuramente una panoramica molto più completa! A proposito, mi piace il tuo suggerimento per (= ~ +). – dukedave

+0

è davvero una risposta completa e completa, vorrei premiare lo sforzo, ma non so se è prassi comune cambiare la "risposta accettata"? Ad ogni modo, sono nuovo di Haskell, e questa risposta mi ha davvero aiutato a capire alcuni principi intelligenti del linguaggio (anche, piccolo errore di battitura all'inizio hai scritto = ~ invece di = ~~) –

7

Non so nulla di Haskell, ma se si utilizza una libreria di espressioni regolari basata su PCRE, è possibile utilizzare i modificatori di modalità all'interno dell'espressione regolare. Per far corrispondere "bossolo" in modo insensibile caso, è possibile utilizzare questa espressione regolare in PCRE: (? I)

(?i)caseless 

Il modificatore modalità sostituisce qualsiasi opzione di insensibilità maiuscole e minuscole o il caso che è stato impostato al di fuori l'espressione regolare. Funziona anche con operatori che non ti permettono di impostare alcuna opzione.

Analogamente, (? S) attiva "modalità linea singola" che fa sì che il punto corrisponda alle interruzioni di riga, (? M) attiva "modalità multi linea" che rende^e $ corrisponde alle interruzioni di riga e (? X) attiva la modalità di spaziatura libera (gli spazi non tagliati e le interruzioni di riga al di fuori delle classi di caratteri sono insignificanti). Puoi combinare le lettere. (? ismx) attiva tutto. Un trattino disattiva le opzioni. (? -i) rende la regex sensibile al maiuscolo/minuscolo. (? x-i) avvia una regex con distinzione tra maiuscole e minuscole.

+0

funziona anche! è molto più semplice ma anche meno generico della soluzione accettata –

+0

+1 Questo ci permette di mantenere l'operatore id = '' e rendere l'espressione regolare definita come 'String'. Molto più semplice! –