2011-09-19 61 views
6

所以我玩这个周围:获取型积分一个的值=>并[a]从一个积分的值=>([A],[A],[A])

factors :: Integral a => a -> [a] 
factors n = filter (\d -> n `rem` d == 0) . takeWhile (\d -> d*d <= n) $ [ 1 .. ] 

abundants_perfects_deficients :: Integral a => ([a],[a],[a]) 
abundants_perfects_deficients = foldr switch ([],[],[]) [1..] 
    where switch :: Integral a => a -> ([a],[a],[a]) -> ([a],[a],[a]) 
     switch n (as,ps,ds) = 
      let t = sum (factors n) in 
       if t < n then (as,ps,n:ds) 
      else if t == n then (as,n:ps,ds) 
      else     (n:as,ps,ds) 

虽然我有abundants_perfects_deficients,我宁愿有三个值:abundants,perfectsdeficients所有类型Integral a -> [a]

一两件事,不工作是:

abundants,perfects,deficients :: Integral a => [a] 
(abundants,perfects,deficients) = abundants_perfects_deficients 

因为这限制了三都是在同一a

我试图事做他们一个接一个,所以他们不会互相制约,但没有工作,要么:

perfects :: Integral a => [a] 
(_,perfects,_) = abundants_perfects_deficients 

因为编译器无法弄清楚如何转换类型forall a. Integral a => ([a],[a],[a])的值为(t1, forall a. Integral a => [a], t2)

这似乎足够的cromulent。

现在我知道我可以单独执行这些(只是perfects = filter isPerfect [1..]),或将其约束到所有相同类型的((abundants,perfects,deficients) = abundants_perfects_deficients工作正常,如果abundants,perfects,deficients :: [Integer]),但

  • 我喜欢使用共享信息来建立所有三个
  • 我想不只是被限制为Integer小号

想法?


编辑:让人着迷足够的这个作品:

abundants :: Integral a => [a] 
abundants = f as 
    where as :: [Integer] 
     (as,_,_) = abundants_perfects_deficients 
     f :: Integral a => [Integer] -> [a] 
     f = map fromInteger 

但这并不:

abundants_perfects_deficients' :: (Integral a,Integral p, Integral d) => ([a],[p],[d]) 
abundants_perfects_deficients' = (f as, f ps, f ds) 
    where as,ps,ds :: [Integer] 
     (as,ps,ds) = abundants_perfects_deficients 
     f :: Integral a => [Integer] -> [a] 
     f = map fromInteger 

abundants,perfects,deficients :: (Integral a) => [a] 
(abundants,perfects,deficients) = abundants_perfects_deficients' 

我不知道为什么。

回答

7

这涉及到多态类型的真正含义是什么,这是稍微比他们第一次的显示更加复杂。

在这一点上可能更容易转换思维方式,并在量词看成是拉姆达抽象形式:像∀ a. [a] A型是这样一个功能采取单一类型的参数,并返回一个列表这种类型的东西。像Integral a这样的类约束可以被视为额外的参数(具体来说,实例字典),当GHC为您找到值时,它们将被隐式提供。

为了强调这一点,我将写出量词为/\ a ->来模仿lambda语法,并将类约束写为常规参数。

用这种方式写的,abundants_perfects_deficients的类型是/\a -> Integral a -> ([a],[a],[a]),并且您的初始尝试失败,主要是因为您试图对双参数函数的结果进行模式匹配。在许多情况下,GHC会自动混洗这些隐含的争论以使事情顺利进行,但这里很明显不能 - 从abundants_perfects_deficients得到任何结果,您首先需要将它应用于两个参数,得到单态结果,然后使用该模式绑定。即使模式只绑定了一个值,其余的仍然是_,但GHC仍然需要对模式绑定本身进行类型检查,所以即使看起来额外的参数可能会浮出到单个绑定的标识符上,也会失败原因是一次绑定所有三个。

要将三个多态值绑定到一个模式,您需要额外的参数放在里面,给abundants_perfects_deficients一个像(/\a -> Integral a -> [a], /\a -> Integral a -> [a], /\a -> Integral a -> [a])这样的类型。这需要the ImpredicativeTypes extension,这有点过去了,我仍然很谨慎。

很多让你跳动的东西是,GHC不够聪明来找出“明显”的东西,例如基于绑定的特定部分内使用的浮动隐式类型和约束参数。考虑到它已经在幕后做了多少魔术,这并不会让我感到困扰。 :]

最简单的解决方案是将所有三个分开绑定,使用选择功能提取单个元素。这可以让顶级绑定以预期的方式呈现多态,并且隐含的参数被隐式地传递给abundants_perfects_deficients,并且投影函数在(现在是单形的)模式匹配之后简单地丢弃其他三个。

3
abundants,perfects,deficients :: Integral a => [a] 
(abundants,perfects,deficients) = abundants_perfects_deficients 

尝试:

fst3 (a,_,_) = a 
snd3 (_,a,_) = a 
thd3 (_,_,a) = a 

abundants,perfects,deficients :: Integral a => [a] 
abundants = fst3 . abundants_perfects_deficients 
perfects = snd3 . abundants_perfects_deficients 
deficients = thd3 . abundants_perfects_deficients 
+0

将无法​​正常工作:'FST ::(A,B) - >一个'(和snd类似),你将不得不提供一些新的三元组访问器;) –

+0

Ops,你是对的。更新以修复它。 – nulvinge

+0

需要删除你的(。)运算符(abundants_perfects_deficients不是函数) – rampion

1

fromIntegral可能是有用的:

Prelude> :t fromIntegral 
fromIntegral :: (Num b, Integral a) => a -> b 
1

可能略有偏差,但无论如何。

factors功能失常(尝试计算factors 28;)

这里有一个不同的解决问题的方法:

classifieds = map (\n -> (n, compare n (sum $ factors n))) [1..] 

perfects = map fst $ filter ((== EQ) . snd) classifieds 
abundants = map fst $ filter ((== GT) . snd) classifieds 
deficients = map fst $ filter ((== LT) . snd) classifieds 
+0

哎呦:)也是好主意。 – rampion

相关问题