compress xs@(_:_:_) = (ifte <$> ((==) <$> head <*> head.tail) <$> ((compress.).(:) <$> head <*> tail.tail) <*> ((:) <$> head <*> compress.tail) ) xs
导致类型错误,但我看不出原因。它应该等同于
compress xs@(_:_:_) = (ifte (((==) <$> head <*> head.tail) xs) (((compress.).(:) <$> head <*> tail.tail) xs) (((:) <$> head <*> compress.tail) xs))
,但没有。
注意:ifte = (\ x y z -> if x then y else z)
,<$>
和<*>
来自Control.Applicative
。
编辑:错误是:
Couldn't match expected type `[a]' with actual type `[a] -> [a]'
In the expression:
(ifte <$> ((==) <$> head <*> head . tail)
<$>
((compress .) . (:) <$> head <*> tail . tail)
<*>
((:) <$> head <*> compress . tail))
$ xs
In an equation for `compress':
compress xs@(_ : _ : _)
= (ifte <$> ((==) <$> head <*> head . tail)
<$>
((compress .) . (:) <$> head <*> tail . tail)
<*>
((:) <$> head <*> compress . tail))
$ xs
我在尝试为Ninety-Nine Haskell Problems的问题8编写无点解决方案时遇到了这个问题。我试图通过修改我写的有点解决方案来做到这一点,这是
compress::Eq a => [a]->[a]
compress [] = []
compress (x:[]) = (x:[])
compress (x:y:xs) = ifte ((==) x y) (compress (x:xs)) (x:(compress (y:xs)))
答案 0 :(得分:6)
首先,缩进。其次,考虑使用一些变量。
即使有更明智的格式,你也可以看到它是
compress =
ifte <$> ((==) <$> head <*> head.tail)
<$> ((compress.).(:) <$> head <*> tail.tail)
<*> ((:) <$> head <*> compress.tail)
什么时候应该
compress =
ifte <$> ((==) <$> head <*> head.tail)
<*> ((compress.).(:) <$> head <*> tail.tail)
<*> ((:) <$> head <*> compress.tail)
第三,即使你必须是不可思议的,怎么样
compress (x:r@(y:_)) = ifte (x==y) id (x:) $ compress r
或,免费点
compress = map fst . filter (uncurry (/=)) . (zip <$> id <*> tail)
答案 1 :(得分:5)
这是以更易读的方式编写的代码
{-# LANGUAGE NoMonomorphismRestriction #-}
import Control.Applicative
u = ((==) <$> head <*> head.tail)
v = ((compress.).(:) <$> head <*> tail.tail)
w = ((:) <$> head <*> compress.tail)
ifte = (\ x y z -> if x then y else z)
--compress xs@(_:_:_) = (ifte <$> u <$> v <*> w) xs
compress xs@(_:_:_) = (ifte (u xs) (v xs) (w xs))
我希望你现在看到错误 - 正确的版本是
--compress xs@(_:_:_) = (ifte <$> u <*> v <*> w) xs
答案 2 :(得分:2)
这与已经说过的内容非常相似,但放纵了我一会儿,并允许我向你传讲类型的价值。
import Control.Applicative
ifte :: Bool -> a -> a -> a
ifte b t f = if b then t else f
compress :: Eq a => [a] -> [a]
-- compress = ifte <$> cond <$> t <*> f
-- We will leave compress undefined so we can load this into ghci.
-- After some trial and error it is clear that this is the part
-- that doesn't type check
compress = undefined
cond :: Eq a => [a] -> Bool
cond = (==) <$> head <*> head . tail
t :: Eq a => [a] -> [a]
t = (compress .) . (:) <$> head <*> tail . tail
f :: Eq a => [a] -> [a]
f = (:) <$> head <*> compress . tail
在这里我把它拆分了,正如布兰登所说,此时你应该看到错误在于使用<$>
应该是<*>
。当你不断学习应用风格时,你会对这个概念感到满意,你的表达通常只有一个<$>
跟随任意#<*>
:
f <$> a <*> b <*> c <*> d <*> ...
但是,忽略这种洞察力,我暂时给了每个子表达式一个类型和一个TLD。这让我可以将文件加载到ghci并稍微玩一下。
ghci> :t ifte <$> cond <$> t <*> f
... Eq a => [a] -> [a] -> [a]
WTF ???这是声音???它本来应该是一个错误,但显然这个表达是好的。或者是吗?请注意,此类型的签名与compress
所需的签名不匹配。
ghci> :t compress
... Eq a => [a] -> [a]
子表达式匹配我们期望它们的类型签名,正如编译器没有呕吐我们所证明的那样。既然如此,问题显然就在于我们将它们结合起来的方式。那么我们想要在这里结合什么呢?忽略Eq约束:
ifte :: Bool -> [a] -> [a] -> [a]
cond :: [a] -> Bool
t :: [a] -> [a]
f :: [a] -> [a]
-- desired result
:: [a] -> [a]
在这里,我为ifte
而不是任何[a]
进行了a
的琐碎专业化。关系很明确:cond
,t
和f
的输出类型与ifte
的输入类型相匹配。我们只需要将所有这三个表达式都相同[a]
。认识到(input ->)
是适用的,我们概括:
arg1 :: (a -> b -> c -> d)
arg2 :: f a
arg3 :: f b
arg4 :: f c
res :: f d
-- for our case,
-- f = ([a] ->)
-- a = Bool
-- b = [a]
-- c = [a]
-- d = [a]
停止...... Hoogle时间! Hoogling (a -> b -> c -> d) -> f a -> f b -> f c -> f d
,我们立即找到liftA3
,这一点并不奇怪:
liftA3 f a b c = f <$> a <*> b <*> c