0

我承认这是我的功课。但是我努力了之后真的找不到好的解决方案。

可能有一些愚蠢的方法可以做到这一点,例如:

myHead (x:[]) = x
myHead (x:y:xs) = fst (x, y)

但我认为这不是老师想要的。
顺便说一句,不需要错误处理。

提前致谢!

4

3 回答 3

4

有一个非常自然的函数不在前奏中,称为“uncons”,它是 uncurried cons 的逆函数。

cons :: a -> [a] -> [a]
uncurry cons :: (a, [a]) -> [a]

uncons :: [a] -> (a, [a])
uncons (x:xs) = (x, xs)

您可以使用它来实现 head 作为

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的更好配对headtail

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)

两者互为倒数。

现在我们可以通过获取返回元组的第一个元素来获取headfor sStreamuncons

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
于 2013-11-04T14:34:48.930 回答
2

也许您应该写入自己的 cons List 类型,那么它可能更有意义。虽然类型同义词不能递归,所以你最终使用非元组数据构造函数,使元组变得多余......它看起来像:

data List a = Nil | List (a, List a)
        deriving( Show )

head :: List a -> a
head (List c) = fst c
于 2013-11-04T15:26:58.750 回答
1

就像评论中已经说过的那样,这只是一个愚蠢的任务,你不会得到你可以称之为良好实现的东西head

对于这些要求,您的解决方案很好 - 因为我将替换的唯一更改(x:y:xs)因为(x:y:_)根本xs没有使用(这实际上会在某些设置中导致编译器警告)。事实上,你也可以这样做y

myHead (x:_:_) = fst (x, undefined)

会有一些看起来可能不是那么无用的替代方法fst不要只是手动构建一个元组并立即再次解构它:

myHead' [x] = x
myHead' xs = myHead' . fst $ splitAt 1 xs

myHead'' = foldr1 $ curry fst

myHead''' = fromJust . find ((==0) . fst) . zip [0..]

但你可以正确地说这些都是荒谬的。

于 2013-11-04T13:41:24.220 回答