我正在编写一个Haskell函数,它接受一个字符串列表并返回一个包含前两个字符串的列表作为结果的元组。因此,示例输出将是:
listtuple ["bride", "zilla", "crazy", "women"] = [("bride", "villa")]
listtuple ["basketball", "football"] = [("basketball", "football")]
我想要接近它的方式如下:
listtuple :: Eq a => [Str a] -> [(Str a, Str a)]
listtuple xs = [(x,y) | x <- xs !! 0, y <- xs !! 1]
基本上我认为我可以只选择列表的第一个和第二个索引中的元素,但是我遇到了错误。这里有什么帮助吗?
答案 0 :(得分:10)
简单的答案是
listtuple :: [a] -> [(a,a)]
listtuple (x:y:_) = [(x,y)]
listtuple _ = []
由于您的列表将始终包含一个项目或不包含任何项目,因此最好使用Maybe
来完全实现此目的。
listtuple2 :: [a] -> Maybe (a,a)
listtuple2 (x:y:_) = Just (x,y)
listtuple2 _ = Nothing
答案 1 :(得分:7)
你可能想做的是:
listtuple xs =
let x = xs !! 0
y = xs !! 1
in (x, y)
请注意,这与您所写的内容不同。原因是您编写的列表理解转换为以下内容:
do x <- xs !! 0 -- Treat the first element of xs as a list
y <- xs !! 1 -- Treat the second element of xs as a list
return (x, y)
这说明了问题:当您将xs !! 0
和xs !! 1
列为非列表时,您将其视为列表。 xs !! 0
只是一个元素,因此如果您要声明x
等于xs !! 0
,请使用:
let x = xs !! 0
in <some expression that uses x>
列表推导语法中的<-
不会做同样的事情,我建议您避开列表推导,直到您理解列表monad的工作原理,因为编译器会将列表推导转换为列表monad。
现在,第二个问题是您使用(!!)
。你应该避开像(!!)
那样的部分功能,并专注于使用模式匹配来解决这些问题。做你要求的惯用方法是在前两个元素上进行模式匹配:
listtuple (x:y:_) = (x, y)
...除了在包含少于两个元素的列表上失败。您可以通过将结果存储为Maybe
来防止这种情况,其中Just
包装成功结果,Nothing
表示失败:
listtuple :: [a] -> Maybe (a, a)
listtuple (x:y:_) = Just (x, y)
listtuple _ = Nothing
答案 2 :(得分:1)
我可以看到您的代码中有一些错误,但如果您对代码无效的具体问题请告诉我。
首先,您提供的类型签名无效。 String
是一个无参数的数据结构,Str a
什么都不是,除非你自己定义了一个名为Str
的数据结构。 String
的列表不需要您提供的等式约束,因为编译器已经知道String
是Eq
的实例。
其次,您正在使用列表理解语法(看起来像什么)where子句的位置。考虑Haskell将在绑定元素xs !! 0
之前尝试评估x
。因为这是一个列表理解,所以这可能不会立即失败,因为xs
是String
的列表(实际上是Char
的列表),但最终会得到{{{}}的元组1}}从xs中的第一个和第二个字符串中拉出来。
这是一个使用模式匹配的简单解决方案,可以产生您想要的效果。
Char
请注意,这不是一个完整的函数(即,如果传递一个包含少于两个元素的列表,则会导致错误)。当一个空列表或一个元素列表传递给它时,可能会通过返回空列表来实现这一点,这是否适用于您的预期目的?
这是您可能会发现的替代版本,适合您用于编写自己版本的心理模型。
listtuple :: [a] -> [(a, a)]
listtuple (x:y:zs) = [(x, y)]