我承认这是我的功课。但是在努力工作之后,我真的找不到一个好的解决方案。
可能有一些愚蠢的方法可以实现这一点,例如:
myHead (x:[]) = x
myHead (x:y:xs) = fst (x, y)
但我认为这不是老师想要的。
顺便说一下,不需要进行错误处理。
提前致谢!
答案 0 :(得分:4)
有一种非常自然的功能,不在前言中称为“uncons”,这是未经证实的缺点的反面。
cons :: a -> [a] -> [a]
uncurry cons :: (a, [a]) -> [a]
uncons :: [a] -> (a, [a])
uncons (x:xs) = (x, xs)
您可以使用它来实现头部
head = fst . uncons
为什么uncons
自然?
您可以将列表视为通过使用两个构造函数定义的数据类型
nil :: [a]
nil = []
cons :: (a, [a]) -> [a]
cons (a,as) = a:as
您还可以将其视为由函数
解构的数据类型destruct :: [a] -> Maybe (a, [a])
destruct [] = Nothing
destruct (a:as) = Just (a, as)
解释为什么那些明确地与列表类型相关联,这远远超出了这个答案,但是看待它的一种方法是尝试定义
nil :: f a
cons :: (a, f a) -> f a
或
destruct :: f a -> Maybe (a, f a)
对于任何其他容器类型f
。你会发现他们都与列表关系密切。
在uncons
的定义的第二种情况下,您几乎已经可以看到destruct
了,但路上有Just
。 uncons
最好与head
和tail
配对,但未在空列表中定义
head [] = error "Prelude.head"
因此我们可以调整之前的答案,以便为无限流工作。在这里,我们可以将无限流视为由一个函数
构造data Stream a = Next a (Stream a)
cons :: (a, Stream a) -> Stream a
cons (a, as) = Next a as
并由一个函数破坏
uncons :: Stream a -> (a, Stream a)
uncons (Next a as) = (a, as)
-- a. k. a.
uncons stream = (head stream, tail stream)
这两个人彼此相反。
现在,我们可以通过head
Stream
uncons
head = fst . uncons
这就是head
中的Prelude
模型,所以我们可以假装列表是无限流,并以这种方式定义头
uncons :: [a] -> (a, [a])
uncons (a:as) = (a, as)
-- a. k. a.
uncons list = (head list, tail list)
head = fst . uncons
答案 1 :(得分:2)
也许您希望写入自己的缺点列表类型,然后可能更有意义。虽然类型同义词不能递归,所以你最终使用非元组数据构造函数,使元组变得多余..它看起来像:
data List a = Nil | List (a, List a)
deriving( Show )
head :: List a -> a
head (List c) = fst c
答案 2 :(得分:1)
就像在评论中已经说过的那样,这只是一个愚蠢的任务,你不会得到一些你称之为head
的良好实现的东西。
对于这些要求,您的解决方案很好 - 作为唯一的更改我将(x:y:xs)
替换为(x:y:_)
,因为根本不使用xs
(这实际上会导致某些设置中的编译器警告)。事实上,您也可以使用y
执行此操作:
myHead (x:_:_) = fst (x, undefined)
有些替代方案看起来可能不是相当所以useless use of fst
,即不只是手工构建元组并立即再解构它:
myHead' [x] = x
myHead' xs = myHead' . fst $ splitAt 1 xs
myHead'' = foldr1 $ curry fst
myHead''' = fromJust . find ((==0) . fst) . zip [0..]
但你可以理所当然地说这些只是荒谬的。