【问题标题】:Vowel datatype in Haskell, is it possible?Haskell中的元音数据类型,有可能吗?
【发布时间】:2011-11-29 05:37:08
【问题描述】:

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

   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

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

【问题讨论】:

  • 我不知道 UNIX 名称中不允许使用元音 ;)
  • 为什么需要“元音数据类型”?它会做什么?
  • @Anschel,大概是一个Char,其值只能采用元音。这将是一种声明unixname 的正确性标准之一的方法。呃……实际上不是因为他需要它的补充,而是你明白了。
  • 这只是我CS课上的一个练习,不知道为什么他叫它“unixname”,用于删除元音的功能。
  • 你也可以使用unixname = filter consonant where consonant = not . vowel

标签: haskell types functional-programming pattern-matching


【解决方案1】:

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

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

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构造函数没有被导出,新的Vowels只能由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 中的新类型非常轻量级*,但程序员确实必须编写和使用代码来进行包装和展开。在许多情况下,收益超过了额外的工作。但是,我真的想不出任何重要的应用程序,知道您的String 是无元音的,所以我可能只使用普通的String

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

【讨论】:

    【解决方案2】:

    您可以使用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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-10
      • 2011-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-20
      相关资源
      最近更新 更多