Haskell 如何处理将函数应用于不正确的值?

Haskell 如何处理将函数应用于不正确的值?,haskell,Haskell,如果我有数据,但我不确定这是一个整数还是一个字符串,但我想对它应用(+1),如果它是一个整数很好,但如果它是一个字符串-什么都不做,我将如何处理它?这就是什么都没有出现的地方吗?如果您有一个值可以是两种类型中的一种,您可以使用或类型来表示该值或者将另外两种类型作为参数,因此或者a b是属于a类型或者b类型的值。这两种情况由两个不同的构造器Left和Right标识,这两个构造器表示这两种情况的左(a)或右(b)类型。在本例中,您可以使用任一字符串整数。要操作这些值,可以使用模式匹配。比如说, fo

如果我有数据,但我不确定这是一个整数还是一个字符串,但我想对它应用(+1),如果它是一个整数很好,但如果它是一个字符串-什么都不做,我将如何处理它?这就是
什么都没有
出现的地方吗?

如果您有一个值可以是两种类型中的一种,您可以使用
类型来表示该值
或者
将另外两种类型作为参数,因此
或者a b
是属于
a
类型或者
b
类型的值。这两种情况由两个不同的构造器
Left
Right
标识,这两个构造器表示这两种情况的左(
a
)或右(
b
)类型。在本例中,您可以使用
任一字符串整数
。要操作这些值,可以使用模式匹配。比如说,

foo :: Either String Int -> Either String Int
foo (Left s) = Left s
foo (Right n) = Right (n+1)

实现您在问题中询问的转换。

好吧,Haskell函数是强类型的,这意味着它们指定输入和输出的类型。为了在一开始就接受多个类型的值,您需要使用
将它们保存在同一类型中。因此,例如,如果要接收
字符串
整数
,则函数必须具有以下类型:

f :: Either String Integer -> ...
然后,编写函数的方法是在
上进行模式匹配,以查看收到的值的类型。你可以这样写:

-- str is a String
f (Left  str) -> ... 
-- int is an Integer
f (Right int) -> ...
list = [Right 1, Left "a", Right 2]
因此,最简单的方法是,如果数字是
整数
,则只增加该数字,但保持
字符串
不变。你可以这样写:

-- 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有一个很好的技巧来避免上面的样板,那就是从
Functor
类中使用
fmap
。这使我们能够自动在
右侧
一半
上编写函数,同时完全忽略
左侧
一半中的任何内容,如下所示:

f = fmap (+1)
如果我们推断出上述函数的类型,它实际上是:

f :: (Functor f, Num a) => f a -> f a
但是在我们的例子中,我们可以通过将
a
设置为
Integer
f
设置为
任一字符串来专门化类型:

f :: Either String Int -> Either String Int
换句话说,
任一字符串
都是
函子
类实例的一个示例,其中有许多

但是,请注意,要在整数或字符串上使用此函数,必须首先将它们包装在
构造函数中。例如,这些将不会进行类型检查:

f 1   -- WRONG!
f "a" -- WRONG!
但这些将:

f (Right  1) -- Correct!
f (Left "a") -- Correct!
这意味着,如果你有一个整数列表和一个字符串,你将不得不这样写:

-- str is a String
f (Left  str) -> ... 
-- int is an Integer
f (Right int) -> ...
list = [Right 1, Left "a", Right 2]
请注意,如果尝试在列表中混合整数和字符串,则会出现类型错误:

list = [1, "a", 2] -- WRONG!
然后我们可以在第一个正确的
列表上映射
f
,以获得:

map f list = [Right 2, Left "a", Right 3]
我们不想随意混合类型 你说

假设我有一个列表,但是这个列表可以是[“a”]或者[1],如果我想在不知道列表中的确切类型的情况下应用一些函数

Haskell不允许您创建这样的列表-列表中的所有元素都必须具有相同的类型。你可以有
[1,2,3]
[“a”,“cheese”,“4”]
,但不能有
[“a”,1]
。Haskell将确保只应用与列表中的元素一起工作的函数。Haskell和您将始终能够计算出列表中元素的类型。这种类型知识被称为静态类型,一开始它看起来不必要的僵化,但实际上非常灵活。黄金法则是:编译器始终知道您拥有的数据

我如何处理类型问题

你不会遇到这样的类型问题这就是静态类型的乐趣所在——在数据类型或在数据类型之间转换时出错的程序不会编译——您会在代码运行之前发现错误

也许你有一个数据流,可能会混合字符串和数字。你需要问的第一个问题是“我想用它们做什么,我真的需要把它们混合在一起吗?”。有没有更好的办法把它们分开?例如,在Haskell中,编写功能齐全的静态类型解析器非常容易(如果需要的话,可以查找parsec)


新:忽略非整数 (重读你的问题标题,也许这就是你想要的。)

很容易忽略非整数。正如您所提到的,您可以使用
Nothing
,而其对应的
只使用
,如下所示

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
来转换每个值,但是您需要导入Network.CGI.Protocol(maybeRead)
,或者因为它无权不在
数据中。可能
,请复制并粘贴代码:

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]
但是,也许你不希望所有的
都没有
/
只是
绒毛,而希望只得到整数。 如果为
[a]->[a]
选择,您将从
数据中找到
catMaybes
。可能
模块,因此让我们导入数据。可能
然后定义

intsOnly :: [String] -> [Int]
intsOnly = catMaybes.map maybeRead  -- read them, ignore Nothings and unwrap Justs
所以当你做
intsOnly myData

[45,4,6]
然后,如果你想在它们中添加一个,你可以这样做

map (+1) (intsOnly myData)
[Left 5, Right "a", Right "hello", Left 6]
那是

[46,5,7]
根据需要

有序地混合类型 如果必须混合整数和字符串,可以这样做

map (+1) (intsOnly myData)
[Left 5, Right "a", Right "hello", Left 6]
这是一个由
Int
eger或
String
s组成的列表,因此类型为
Int-String
,但可能您希望混合的不仅仅是数字和文本,可能您想要整数、字符串、时间或字符串列表。然后,您可以自己滚动:

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"]
(注意Haskell区分大小写;
MyStuff
是一种类型,
MyString