Wie geben Sie ein Regex-Muster in Haskell an?

Nov 28 2020

Ich versuche, einen regulären Ausdruck durch den folgenden Code zu ersetzen

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

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

Es fiel mir wirklich schwer, eine nachvollziehbare Dokumentation dazu zu finden. Dies erzeugt den Kompilierungsfehler:

    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

Was ist das Problem mit dieser Klammer und wie würde ich das richtig machen? Danke -Skye

Antworten

3 WillemVanOnsem Nov 28 2020 at 18:56

Das […|…|]ist quasi Zitatsyntax [haskell-wiki] . Dies ist eine Erweiterung der Haskell-Syntax und nicht standardmäßig aktiviert.

Sie können dies mit einem LANGUAGEPragma aktivieren:

{-# LANGUAGE QuasiQuotes #-}

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

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

Die Quasiquoten generieren Haskell-Code, der dann im Haskell-Programm verwendet wird. Dies bedeutet, dass durch die Quasiquoten die Validierung des regulären Ausdrucks zur Kompilierungszeit erfolgen kann und die Effizienz im Vergleich zum Kompilieren des regulären Ausdrucks zur Laufzeit sogar geringfügig optimiert werden kann.

Für die gegebene onlyLettersFunktion erhalten wir dann:

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

Die Antwort von Willem Van Onsem ist eine bessere Antwort auf die Frage, aber ich werde eine Antwort vorschlagen , die dies stattdessen versucht .

Auf diese Weise können Sie die Textersetzung in einfachem Haskell durchführen, ohne dass es zu Quasi-Anführungszeichen kommt.

Mit 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"