2011-10-01 68 views
7

he escrito el código siguiente para quitar las vocales de una frase:Tipo de datos de vocales en Haskell, ¿es posible?

main = print $ unixname "The House" 

    vowel x = elem x "aeiouAEIOU" 

    unixname :: [Char] -> [Char] 
    unixname [] = [] 
    unixname (x:xs) | vowel x = unixname xs 
      | otherwise = x : unixname xs 

preguntando si es posible crear un tipo de datos para vocal? El compilador no me deja usar caracteres en un tipo de datos.

+2

yo no era consciente de que las vocales no estaban permitidos en nombres UNIX;) –

+0

¿Por qué quiere un "tipo de datos para vocal"? ¿Qué haría? –

+2

@Anschel, presumiblemente ser un 'Char' cuyos valores solo pueden asumir las vocales. Sería una forma de declarar uno de los criterios de corrección para 'unixname'. Er ... en realidad no porque necesita su complemento, pero entiendes la idea. – luqui

Respuesta

15

No directamente. El problema es que los caracteres son un tipo incorporado sin posibilidad de polimorfismo. Esto es diferente de los literales numéricos, que están diseñados para ser polimórficos a través de la clase de tipo Num.

Dicho esto, hay dos enfoques básicos que puede tomar: un contenedor de tipo nuevo con un constructor inteligente o un tipo totalmente nuevo.

La envoltura newtype es más fácil de usar:

module Vowel (Vowel, vowel, fromVowel) where 

newtype Vowel = Vowel Char 

vowel :: Char -> Maybe (Vowel) 
vowel x | x `elem` "aeiouAEIOU" = Just (Vowel x) 
     | otherwise = Nothing 

fromVowel :: Vowel -> Char 
fromVowel (Vowel x) = x 

Desde el constructor Vowel no se exporta, nuevos Vowel s sólo puede ser creado por la función vowel, que sólo admite los caracteres que desee.

También podría hacer un nuevo tipo de esta manera:

data Vowel = A | E | I | O | U | Aa | Ee | Ii | Oo | Uu 

fromChar :: Char -> Maybe Vowel 
fromChar 'a' = Just Aa 
fromChar 'A' = Just A 
-- etc. 

toChar :: Vowel -> Char 
toChar Aa = 'a' 
toChar A = 'A' 

Esta segunda forma es bastante pesado, y por lo tanto es mucho más difícil de usar.

Así que así es como hacerlo. Aunque no estoy muy seguro de que quieras hacerlo. La expresión habitual es hacer tipos que representan sus datos, y específicamente no representan las vocales. Un patrón común sería algo como esto:

newtype CleanString = Cleaned { raw :: String } 

-- user input needs to be sanitized 
cleanString :: String -> CleanString 

Aquí el newtype distingue entre la entrada unsanitized y desinfectados. Si la única forma de hacer un CleanString es por cleanString, entonces sabrá estáticamente que cada CleanString está desinfectado correctamente (siempre que cleanString sea correcto). En tu caso, parece que realmente necesitas un tipo de consonantes, no de vocales.

Los tipos nuevos en Haskell son muy livianos *, pero el programador tiene que escribir y usar el código para realizar la envoltura y el desenvolver. En muchos casos, los beneficios superan el trabajo adicional. Sin embargo, realmente no puedo pensar en ninguna aplicación en la que sea importante saber que tu String está libre de vocales, así que probablemente solo trabaje con un String simple.

* los tipos nuevos solo existen en tiempo de compilación, por lo que, en teoría, no existe un costo de rendimiento en tiempo de ejecución para su uso. Sin embargo, su existencia puede cambiar el código producido (por ejemplo, inhibir REGLAS), por lo que a veces hay un impacto medible en el rendimiento.

8

Puede usar phantom types para etiquetar caracteres con información adicional, a fin de hacer que el sistema de tipos garantice durante el tiempo de compilación que sus cadenas solo contienen, por ejemplo, vocales o no vocales.

Aquí está un ejemplo de juguete:

{-# LANGUAGE EmptyDataDecls #-} 

import Data.Maybe 

newtype TaggedChar a = TaggedChar { fromTaggedChar :: Char } 

data Vowel 
data NonVowel 

isVowel x = x `elem` "aeiouyAEIOUY" 

toVowel :: Char -> Maybe (TaggedChar Vowel) 
toVowel x 
    | isVowel x = Just $ TaggedChar x 
    | otherwise = Nothing 

toNonVowel :: Char -> Maybe (TaggedChar NonVowel) 
toNonVowel x 
    | isVowel x = Nothing 
    | otherwise = Just $ TaggedChar x 

unixname :: [Char] -> [TaggedChar NonVowel] 
unixname = mapMaybe toNonVowel

La ventaja de este enfoque es que todavía se puede también escribir funciones que trabajan en todos los TaggedChars independientemente de la etiqueta.Por ejemplo:

toString :: [TaggedChar a] -> String 
toString = map fromTaggedChar
Cuestiones relacionadas