Haskell: Как получить "\\ 0" в "\ 0"? - PullRequest
5 голосов
/ 24 августа 2011

Haskell имеет число строковых литералов , которые используют последовательность \ escape. Такие, как \n, \t, \NUL.

Если у меня есть строковый литерал:

let s = "Newline: \\n Tab: \\t"

как определить функцию escape :: String -> String, которая преобразует приведенную выше строку в:

"Newline: \n Tab: \t"

И то же самое со всеми другими строковыми литеральными escape-последовательностями.

Я в порядке с использованием Quasi Quoting и Template Haskell, но не знаю, как их использовать для достижения результата. Есть указатели?


Обновление : Я только что нашел модуль Text.ParserCombinators.ReadP , который включен в базовую библиотеку. Он поддерживает функцию readLitChar :: ReadS Char в Data.Char, которая делает то, что я хочу, но я не знаю, как использовать модуль ReadP. Я попробовал следующее, и это работает:

escape2 [] = []
escape2 xs = case readLitChar xs of
    [] -> []
    [(a, b)] -> a : escape2 b

Но это может быть неправильный способ использования модуля ReadP. Кто-нибудь может дать несколько указателей?

Еще одно обновление : Спасибо всем. Моя последняя функция ниже. Думаю, неплохо.

import Text.ParserCombinators.ReadP
import Text.Read.Lex

escape xs 
    | []      <- r = []
    | [(a,_)] <- r = a
    where r = readP_to_S (manyTill lexChar eof) xs 

Ответы [ 2 ]

6 голосов
/ 24 августа 2011

Вам не нужно ничего делать.Когда вы вводите строковый литерал

let s = "Newline: \\n Tab: \\t"

, вы можете проверить, что это то, что вам нужно:

Prelude> putStrLn s
Newline: \n Tab: \t
Prelude> length s
19

Если вы просто спросите ghci для значения s, вы получитечто-то еще,

Prelude> s
"Newline: \\n Tab: \\t"

, по-видимому, он выполняет некоторое резервное форматирование за вашей спиной, а также отображает кавычки.Если вы позвоните show или print, вы получите еще другие ответы:

Prelude> show s
"\"Newline: \\\\n Tab: \\\\t\""
Prelude> print s
"Newline: \\n Tab: \\t"

Это потому, что show предназначен для сериализации значений, поэтому, когда вы show строка, вы не 'Чтобы получить оригинал обратно, вместо этого вы получаете сериализованную строку, которую можно проанализировать в исходной строке.Результат show s фактически отображается как print s (print определяется как putStrLn . show).Когда вы просто show s в ghci, вы получите еще более странный ответ;здесь ghci форматирует символы, которые сериализованы как show.

tl; dr - всегда используйте putStrLn, чтобы увидеть значение строки в ghci.

Редактировать : Я только что понял, что, возможно, вы хотите преобразовать буквальное значение

Newline: \n Tab: \t

в фактические управляющие последовательности.Самый простой способ сделать это, вероятно, заключить его в кавычки и использовать read:

Prelude> let s' = '"' : s ++ "\""
Prelude> read s' :: String
"Newline: \n Tab: \t"
Prelude> putStrLn (read s')
Newline: 
 Tab:   

Edit 2 : пример использования readLitChar, это очень близко кОтвет Криса за исключением readLitChar:

strParser :: ReadP String
strParser = do
  str <- many (readS_to_P readLitChar)
  eof
  return str

Затем вы запускаете его с помощью readP_to_S, который дает вам список подходящих разборов (не должно быть более одного совпадения, однако может не бытьлюбое совпадение, поэтому вы должны проверить пустой список.)

> putStrLn . fst . head $ readP_to_S strParser s
Newline:
Tab:    
>
2 голосов
/ 24 августа 2011

Запрос о QQ и TH означает, что вы хотите сделать это преобразование во время компиляции.Для простых преобразований String -> Something вы можете использовать буквальное средство OverloadedString в GHC.

EDIT 2 : Использование открытого символьного лексера в Text.Read.Lex

module UnEscape where

import Data.String(IsString(fromString))
import Text.ParserCombinators.ReadP as P
import Text.Read.Lex as L

newtype UnEscape = UnEscape { unEscape :: String }

instance IsString UnEscape where
  fromString rawString = UnEscape lexed
    where lexer = do s <- P.many L.lexChar
                     eof
                     return s
          lexed = case P.readP_to_S lexer rawString of
                    ((answer,""):_) -> answer
                    _ -> error ("UnEscape could not process "++show rawString)

РЕДАКТИРОВАТЬ 1 : Теперь у меня есть лучший экземпляр UnEscape, который использует чтение GHC:

instance IsString UnEscape where
  fromString rawString = UnEscape (read (quote rawString))
    where quote s = '"' : s ++ ['"']

Например:

module UnEscape where

import Data.String(IsString(fromString))

newtype UnEscape = UnEscape { unEscape :: String }

instance IsString UnEscape where
  fromString rawString = UnEscape (transform rawString)
    where transform [] = []
          transform ('\\':x:rest) = replace x : transform rest
          transform (y:rest) = y : transform rest
            -- also covers special case of backslash at end
          replace x = case x of
                        'n' -> '\n'
                        't' -> '\t'
                        unrecognized -> unrecognized

Выше должен быть отдельный модуль от модуля, который использует unEscape:

{-# LANGUAGE OverloadedStrings #-}
module Main where

import UnEscape(UnEscape(unEscape))

main = do
  let s = "Newline: \\n Tab: \\t"
      t = unEscape "Newline: \\n Tab: \\t"
  print s
  putStrLn s
  print t
  putStrLn t

Это производит

shell prompt$ ghci Main.hs 


GHCi, version 7.0.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
[1 of 2] Compiling UnEscape         ( UnEscape.hs, interpreted )
[2 of 2] Compiling Main             ( Main.hs, interpreted )
Ok, modules loaded: Main, UnEscape.
*Main> main
"Newline: \\n Tab: \\t"
Newline: \n Tab: \t
"Newline: \n Tab: \t"
Newline: 
 Tab:   
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...