Haskell中的元音数据类型,有可能吗?

时间:2011-10-01 23:36:52

标签: haskell types functional-programming pattern-matching

我已经编写了以下代码来删除句子中的元音:

   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

只是想知道是否可以为元音创建数据类型?编译器不允许我在数据类型中使用字符。

2 个答案:

答案 0 :(得分:15)

不直接。问题是字符是内置类型,没有多态性。这与数字文字不同,后者通过Num类型类设计为多态。

也就是说,您可以采用两种基本方法:带有智能构造函数的newtype包装器,或者全新的类型。

newtype包装器更易于使用:

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

由于Vowel构造函数未导出,因此新Vowel只能由vowel函数创建,该函数只接受您想要的字符。

你也可以制作一个这样的新类型:

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'

第二种方式非常重要,因此使用起来更加尴尬。

这就是怎么做的。我不太确定你想要的。通常的习惯用法是制作代表您数据的类型,而您明确地代表元音。一个常见的模式是这样的:

newtype CleanString = Cleaned { raw :: String }

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

这里newtype区分未经过消毒和已消毒的输入。如果CleanString的唯一方法是cleanString,那么您静态地知道每个CleanString都已正确清理(假设cleanString正确)。在你的情况下,你似乎确实需要一个辅音类型,而不是元音。

Haskell中的Newtypes非常轻量级*,但程序员必须编写并使用代码来进行包装和解包。在许多情况下,好处超过了额外的工作。但是,我真的想不出任何重要的事情,知道你的String是无元音的,所以我可能只使用普通的String

* newtypes仅在编译时存在,因此理论上使用它们没有运行时性能成本。但是,它们的存在可以改变生成的代码(例如,禁止RULE),因此有时会产生可衡量的性能影响。

答案 1 :(得分:8)

您可以使用phantom types标记带有额外信息的字符,以便在编译期间使字符串系统保证您的字符串仅包含元音或非元音。

这是一个玩具示例:

{-# 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

这种方法的好处是,无论标记如何,您仍然可以编写适用于所有TaggedChars的函数。例如:

toString :: [TaggedChar a] -> String
toString = map fromTaggedChar
相关问题