如果我有数据并且我不确定这是一个整数还是一个字符串,但是我想对它应用(+1),如果它是一个很好的整数,但是如果它是一个字符串 - 什么也不做,怎么做我会处理这个吗?这是Nothing
进来的地方吗?
答案 0 :(得分:8)
嗯,Haskell函数是强类型的,这意味着它们指定了它们的输入和输出的类型。为了甚至首先接受多种类型的值,您需要使用Either
将它们保持在同一类型中。因此,例如,如果您想要接收String
或Integer
,那么您的函数必须具有类型:
f :: Either String Integer -> ...
然后,您编写函数的方式是在Either
上进行模式匹配,以查看您收到的值的类型。你会写一些类似的东西:
-- str is a String
f (Left str) -> ...
-- int is an Integer
f (Right int) -> ...
因此,最简单的方法是,只有Integer
才能增加数字,但保持String
不变。你会这样写:
-- Don't do anything if it is a string
f (Left str) = Left str
-- Increment it if it is an integer
f (Right int) = Right (int + 1)
如果您要求编译器推断出上述函数的类型,您将得到:
f :: Either String Int -> Either String Int
Haskell有一个很好的技巧来避免上面的样板,即使用fmap
类中的Functor
。这使我们可以自动在Right
的{{1}}一半上自动编写函数,同时完全忽略Either
一半中的任何内容,如下所示:
Left
如果我们推断出上述函数的类型,它实际上是:
f = fmap (+1)
但在我们的案例中,我们可以通过将f :: (Functor f, Num a) => f a -> f a
设置为a
并将Integer
设置为f
来专门化该类型:
Either String
换句话说,f :: Either String Int -> Either String Int
是Either String
类实例的一个例子,其中有很多。
但是,请注意,要在整数或字符串上使用此函数,必须先将它们包装在Functor
或Left
构造函数中。例如,这些不会进行类型检查:
Right
但这些会:
f 1 -- WRONG!
f "a" -- WRONG!
这意味着如果你有一个整数列表的字符串,你必须写一个类似的东西:
f (Right 1) -- Correct!
f (Left "a") -- Correct!
请注意,如果尝试在列表中混合整数和字符串,则会出现类型错误:
list = [Right 1, Left "a", Right 2]
然后我们可以将list = [1, "a", 2] -- WRONG!
映射到第一个正确的f
以获取:
list
答案 1 :(得分:5)
你说
所以说我有一个列表但是这个列表可能是[“a”]或者它可能是[1],如果我想在不知道列表中的确切类型的情况下应用某些函数,
Haskell不允许您创建这样的列表 - 列表中的所有元素必须具有相同的类型。您可以[1,2,3]
或["a","cheese","4"]
,但不能["a",1]
。 Haskell将确保仅应用与列表中的元素一起使用的函数。 Haskell和你都将能够计算出列表中元素的类型。这种类型的知识被称为静态类型,起初它似乎是不必要的僵化,但它实际上非常灵活。黄金法则是:编译器始终知道您拥有哪些数据。
我怎么能处理类型问题?
你不会遇到这样的类型问题!这就是静态类型的乐趣 - 那些使用数据类型出错或者在数据类型之间进行转换的程序不会编译 - 在你的代码运行之前你会发现错误!
也许你有一个可能混合字符串和数字的数据流。您需要问的第一个问题是“我想用它做什么,我真的需要将它们混合在一起吗?”。是否有更好的方法将它们分开?例如,在Haskell中,编写功能完备的静态类型解析器非常容易(如果这是你需要的话,查找parsec)。
(在重新阅读你的问题标题时,也许这就是你所追求的。)
忽略非整数很容易。就像你提到的那样,你可以使用Nothing
及其对应的Just
,就像这样
myData = ["nan","45","3454.5","that's not an int, it's a float!","4","6"]
maybeInts :: [String] -> [Maybe Integer] -- type signature needed so Haskell knows you want Integers
maybeInts = map maybeRead
使用方便的函数maybeRead
来转换每个值,但您需要import Network.CGI.Protocol (maybeRead)
或者因为它无权不在Data.Maybe
,所以复制 - 和 - 粘贴代码:
maybeRead :: Read a => String -> Maybe a
maybeRead = fmap fst . listToMaybe . reads
-- take all parses, grab the first one, ignore the remaining text
maybeInts myData
的价值将是
[Nothing, Just 45, Nothing, Nothing, Just 4, Just 6]
但是也许你不希望所有Nothing
/ Just
绒毛,而宁愿只得到整数。
如果[Maybe a] -> [a]
为catMaybes
Data.Maybe
,您会在import Data.Maybe
模块中找到intsOnly :: [String] -> [Int]
intsOnly = catMaybes.map maybeRead -- read them, ignore Nothings and unwrap Justs
,那么让我们intsOnly myData
然后定义
[45,4,6]
所以当你做map (+1) (intsOnly myData)
时,你得到了
[46,5,7]
然后如果你想为它们添加一个,你可以做
[Left 5, Right "a", Right "hello", Left 6]
是
Int
根据需要。
如果你必须混合整数和字符串,你可以
String
这是Either Int String
egers或data MyStuff = MyInt Int | MyString String | MyDate DateTime | MyList FilePath [String]
deriving Show
的列表,类型为mystuff = [MyInt 6, MyString "Hello", MyString "MyString isn't a great name",
MyList "C:/temp/stuff.txt" ["yeah","give","them","problem-specific", "names"],
MyInt 12, MyString "instead"]
,但也许你想混合的不只是数字和文字,也许你想要整数,字符串,时间或字符串列表。你可以自己滚动:
MyStuff
可以在
之类的列表中MyString
(注意Haskell区分大小写; mystuff
是一个类型,My___
是一个类型构造函数(一种将数据包装到一个类型中的函数),report :: MyStuff -> IO ()
report (MyInt n) = mapM_ print [1..n] -- turn the numbers 1..n into print commands, then combine them
report (MyString xs) = print xs -- just print the string.
report (MyList filename contents)
= writeFile filename (unlines contents) -- write them on seperate lines in the file
report _ = return () -- if it's anything else, ignore it and do nothing.
是一个变量。所有变量都是常数!)
您的数据如何最终包裹在mapM_ report mylist
标签中?好吧,也许它开始是一个文件或网络连接或其他东西的整个文本加载,你的解析器将它分成整数,字符串,日期和字符串列表,标记它们(很容易做到)。
...你可以通过编写一个根据它得到的数据定义不同的函数来分隔它们:
{{1}}
现在,如果你有这样的感觉,你可以做到
{{1}}
它会打印一些数字,几个字符串,保存文件,打印更多数字,最后打印一个字符串。
答案 2 :(得分:1)
如果您的值可以是两种类型之一,则可以使用Either
类型作为该值。 Either
将其他两种类型作为参数,因此Either a b
是a
类型或b
类型的值。这两个案例由两个不同的构造函数Left
和Right
标识,表示左侧(a
)或右侧(b
)类型。在您的情况下,您将使用Either String Integer
。要操纵值,您可以使用模式匹配。例如,
foo :: Either String Int -> Either String Int
foo (Left s) = Left s
foo (Right n) = Right (n+1)
实现您在问题中询问的转换。