Come si dichiara un pattern regex in Haskell?

Nov 28 2020

Sto cercando di sostituire una regex con il seguente codice

import Text.RE.Replace
import Text.RE.TDFA.String

onlyLetters :: String -> String
onlyLetters s = replaceAll "" $ s *=~ [re|$([^a-zA-Z])|]

Ho trovato davvero difficile trovare una documentazione comprensibile su questo. Questo produce l'errore di compilazione:

    src\Pangram.hs:6:53: error: parse error on input `]'
  |
6 | onlyLetters s = replaceAll "" $ (s *=~ [re|[a-zA-Z]|]) | ^ Progress 1/2 -- While building package pangram-2.0.0.12 (scroll up to its section to see the error) using: C:\sr\setup-exe-cache\x86_64-windows\Cabal-simple_Z6RU0evB_3.0.1.0_ghc-8.8.4.exe --builddir=.stack-work\dist\29cc6475 build lib:pangram test:test --ghc-options " -fdiagnostics-color=always" Process exited with code: ExitFailure 1 PS C:\Users\mcleg\Exercism\haskell\pangram> stack test pangram> configure (lib + test) Configuring pangram-2.0.0.12... pangram> build (lib + test) Preprocessing library for pangram-2.0.0.12.. Building library for pangram-2.0.0.12.. [1 of 2] Compiling Pangram src\Pangram.hs:7:56: error: parse error on input `]' | 7 | onlyLetters s = replaceAll "" $ s *=~ [re|$([^a-zA-Z])|]
  |                                                        ^

Progress 1/2

--  While building package pangram-2.0.0.12 (scroll up to its section to see the error) using:
      C:\sr\setup-exe-cache\x86_64-windows\Cabal-simple_Z6RU0evB_3.0.1.0_ghc-8.8.4.exe --builddir=.stack-work\dist\29cc6475 build lib:pangram test:test --ghc-options " -fdiagnostics-color=always"
    Process exited with code: ExitFailure 1

Qual è il problema con quella staffa e come lo farei correttamente? Grazie -Skye

Risposte

3 WillemVanOnsem Nov 28 2020 at 18:56

La sintassi[…|…|] è quasi citazione [haskell-wiki] . Questa è un'estensione della sintassi di Haskell e non è abilitata per impostazione predefinita.

Puoi LANGUAGEattivarlo con un pragma:

{-# LANGUAGE QuasiQuotes #-}

import Text.RE.Replace
import Text.RE.TDFA.String

onlyLetters :: String -> String
onlyLetters s = replaceAll "" $ s *=~ [re|$([^a-zA-Z])|]

Le quasi virgolette genereranno codice Haskell e questo viene quindi utilizzato nel programma Haskell. Ciò significa che attraverso le quasi citazioni, la convalida della regex può essere eseguita in fase di compilazione e potrebbe persino ottimizzare leggermente l'efficienza rispetto alla compilazione della regex in fase di esecuzione.

Per la onlyLettersfunzione data , otteniamo quindi:

*Main> onlyLetters "fo0b4r"
"fobr"
2 JamesBrock Nov 29 2020 at 21:02

La risposta di Willem Van Onsem è una risposta migliore alla domanda, ma suggerirò una risposta "prova questo invece" .

È così che puoi sostituire il testo in Haskell semplice senza la complicazione di espressioni regolari quasi citate.

Con https://hackage.haskell.org/package/replace-megaparsec/docs/Replace-Megaparsec.html#v:streamEdit

{-# LANGUAGE TypeFamilies #-}

import Text.Megaparsec
import Text.Megaparsec.Char
import Replace.Megaparsec
import Data.Void

-- | Invert a single-token parser “character class”.
-- | For example, match any single token except a letter or whitespace: `anySingleExcept (letterChar <|> spaceChar)`
anySingleExcept :: (MonadParsec e s m, Token s ~ Char) => m (Token s) -> m (Token s)
anySingleExcept p = notFollowedBy p *> anySingle

-- | A parser monad pattern which matches anything except letters.
nonLetters :: Parsec Void String String
nonLetters = many (anySingleExcept letterChar) 

onlyLetters :: String -> String
onlyLetters = streamEdit nonLetters (const "")

onlyLetters "fo0b4r"
"fobr"