ice*_*man 8 string polymorphism parsing haskell
TL; DR:
如何在返回类型中编写多态的函数?我正在练习,其中任务是写一个函数,它是能够分析的String,并根据其内容,生成或者是Vector [Int],Vector [Char]或Vector [String].
更长版本:
以下是一些预期函数的行为示例:
该字符串"1 2\n3 4"将生成一个Vector [Int]由两个列表组成的字符串:[1,2]和[3,4].
该字符串"'t' 'i' 'c'\n't' 'a' 'c'\n't' 'o' 'e'"将生成一个Vector [Char](即,由列表组成"tic","tac"和"toe").
该字符串"\"hello\" \"world\"\n\"monad\" \"party\""将生成一个Vector [String](即,["hello","world"]和["monad","party"]).
错误检查/异常处理不是此特定练习的关注点.在这个阶段,所有的测试都是纯粹的,即,这不是IOmonad 的范畴.
到目前为止我所拥有的:
我有一个函数(和新的数据类型),它能够对字符串进行分类.我也有函数(每个函数一个Int,Char和String),可以将字符串转换为必要的Vector.
我的问题:如何将这三个转换函数组合成一个函数?
我尝试过的:
(如果我将三个转换函数填充到单个函数中(即,使用case..of结构来VectorType对字符串进行模式匹配),显然不会进行类型检查.
我尝试创建一个Vectorable类并为每种类型定义一个单独的实例; 我很快意识到这种方法只有在函数的参数因类型而异时才有效.在我们的例子中,参数的类型不会改变(即,它始终是a String).
我的代码:
一些评论
解析:该mySplitter对象和mySplit函数处理解析.无可否认,这是一个基于Splitter类型和split功能的粗解析器Data.List.Split.Internals.
分类:该classify函数能够VectorType根据字符串确定最终结果.
转换:在toVectorNumber,toVectorChar和toVectorString功能都能够为一个字符串输入转换Vector [Int],Vector [Char]并Vector [String]分别.
作为旁注,我正在CorePrelude根据导师的推荐进行尝试.这就是为什么你会看到我使用普通Prelude函数的通用版本.
码:
import qualified Prelude
import CorePrelude
import Data.Foldable (concat, elem, any)
import Control.Monad (mfilter)
import Text.Read (read)
import Data.Char (isAlpha, isSpace)
import Data.List.Split (split)
import Data.List.Split.Internals (Splitter(..), DelimPolicy(..), CondensePolicy(..), EndPolicy(..), Delimiter(..))
import Data.Vector ()
import qualified Data.Vector as V
data VectorType = Number | Character | TextString deriving (Show)
mySplitter :: [Char] -> Splitter Char
mySplitter elts = Splitter { delimiter = Delimiter [(`elem` elts)]
, delimPolicy = Drop
, condensePolicy = Condense
, initBlankPolicy = DropBlank
, finalBlankPolicy = DropBlank }
mySplit :: [Char]-> [Char]-> [[Char]]
mySplit delims = split (mySplitter delims)
classify :: String -> VectorType
classify xs
| '\"' `elem` cs = TextString
| hasAlpha cs = Character
| otherwise = Number
where
cs = concat $ split (mySplitter "\n") xs
hasAlpha = any isAlpha . mfilter (/=' ')
toRows :: [Char] -> [[Char]]
toRows = mySplit "\n"
toVectorChar :: [Char] -> Vector [Char]
toVectorChar = let toChar = concat . mySplit " \'"
in V.fromList . fmap (toChar) . toRows
toVectorNumber :: [Char] -> Vector [Int]
toVectorNumber = let toNumber = fmap (\x -> read x :: Int) . mySplit " "
in V.fromList . fmap toNumber . toRows
toVectorString :: [Char] -> Vector [[Char]]
toVectorString = let toString = mfilter (/= " ") . mySplit "\""
in V.fromList . fmap toString . toRows
Run Code Online (Sandbox Code Playgroud)
lef*_*out 16
Haskell不支持协变多态,如果是的话就不会有用.
基本上所有人都可以回答这个问题.现在为什么会这样.
像OO语言一样"返回多态值"并不好,因为返回任何值的唯一原因是在其他函数中使用它.现在,在OO语言中,你没有函数,只有对象附带的方法,所以"返回不同类型"很容易:每个都有内置的合适方法,并且每个实例都可以变化.(这是一个好主意是另一个问题.)
但在Haskell中,功能来自其他地方.他们不知道特定实例的实现更改,因此可以安全地定义这些函数的唯一方法是了解每个可能的实现.但是如果你的返回类型真的是多态的,那是不可能的,因为多态是一个"开放"的概念(它允许随后添加新的实现变种).
相反,Haskell有一个非常方便且完全安全的机制来描述一组封闭的"实例" - 你实际上已经自己使用它了!抽象数据类型.
data PolyVector = NumbersVector (Vector [Int])
| CharsVector (Vector [Char])
| StringsVector (Vector [String])
Run Code Online (Sandbox Code Playgroud)
这是你想要的返回类型.这个函数不会是多态的,它只会返回一个更通用的类型.
现在...... 实际上,Haskell确实有办法对"多态返回"进行排序.在OO中,当您声明返回指定类的子类时.好吧,你不能在Haskell中"返回一个类",你只能返回类型.但是那些可以用来表达"......的任何实例".它被称为存在量化.
{-# LANGUAGE GADTs #-}
data PolyVector' where
PolyVector :: YourVElemClass e => Vector [e] -> PolyVector'
class YourVElemClass where
...?
instance YourVElemClass Int
instance YourVElemClass Char
instance YourVElemClass String
Run Code Online (Sandbox Code Playgroud)
我不知道这对你来说是否有点吸引力.事实是,它更复杂,使用起来更加困难; 你不仅可以直接获得任何可能的结果,而且只能通过方法来利用这些元素YourVElemClass.GADT在某些应用中可能非常有用,但这些通常涉及具有非常深刻的数学动机的类.YourVElemClass似乎没有这样的动机,所以使用简单的ADT替代方案比使用存在量化更好.
Luke Palmer有一个着名的反对存在的咆哮(注意他使用另一种语法,存在性特定的,我认为它是过时的,因为GADT更严格一般).
简单,使用总和类型!
data ParsedVector = NumberVector (Vector [Int]) | CharacterVector (Vector [Char]) | TextString (Vector [String]) deriving (Show)
parse :: [Char] -> ParsedVector
parse cs = case classify cs of
Number -> NumberVector $ toVectorNumber cs
Character -> CharacterVector $ toVectorChar cs
TextString -> TextStringVector $ toVectorString cs
Run Code Online (Sandbox Code Playgroud)