显示列表中主要索引的所有数字 - haskell - 过滤错误

时间:2018-01-21 19:43:10

标签: list haskell fibonacci

这是一堂课 我们应该编写3个函数:

1:打印fibbonaci数字列表 2:打印素数列表 3:打印索引为素数的斐波纳契数列表

EG:让这是fibbonaci系列

然后在partC中 - 仅显示某些元素

1:1

* 2:1(显示为索引2为素数)

* 3:2(显示为指数3为素数)

4:3

* 5:5(显示)

6:8

* 7:13(显示为索引7素数等等)

我完成了第1部分和第1部分。 2但是我正在努力学习第3部分。我创建了一个函数listNum,它创建了一种来自Fibbonaci系列的映射[Integer,Integer] - 其中1st Int是索引,第二个int是实际的fibbonaci数。

现在我的函数partC试图通过过滤索引来拼接斐波那契系列的snd元素,但我在过滤步骤中做错了。

任何帮助都会受到赞赏,因为我是Haskell的初学者。 谢谢!

   fib :: [Integer]
   fib = 0 : 1 : zipWith (+) fib (tail fib)

   listNum :: [(Integer, Integer)]
   listNum = zip [1 .. ] fib

   primes :: [Integer]
   primes = sieve (2 : [3,5 ..])

        where
            sieve (p:xs) = p : sieve [x | x <- xs , x `mod` p > 0]

   partC :: [Integer]  -- Problem in filter part of this function
   partC = map snd listNum $ filter (\x -> x `elem` primes) [1,2 ..]

   main = do
      print (take 10 fib)    -- Works fine
      print (take 10 primes) --works fine
      print (take 10 listNum) --works fine
      print ( take 10 partC) -- Causes error 

错误:

prog0.hs:14:9: error:
    • Couldn't match expected type ‘[Integer] -> [Integer]’
              with actual type ‘[Integer]’
    • The first argument of ($) takes one argument,
     but its type ‘[Integer]’ has none
     In the expression:
    map snd listNum $ filter (\ x -> x `elem` primes) [1, 2 .. ]
  In an equation for ‘partC’:
      partC
        = map snd listNum $ filter (\ x -> x `elem` primes) [1, 2 .. ]



  |
    14 | partC = map snd listNum $ filter (\x -> x `elem` primes) [1,2 ..]

1 个答案:

答案 0 :(得分:1)

以下是我认为您打算作为partC的原始逻辑。你的语法大部分都是正确的,但逻辑有一个缺陷。

partC = snd <$> filter ((`elem` primes) . fst) (zip [1..] fib)
-- note that (<$>) = fmap = map, just infix
-- list comprehension
partC = [fn | (idx, fn) <- zip [1..] fib, idx `elem` primes]

但这不起作用。正如@DanRobertson指出的那样,你会尝试检查4 `elem` primes并遇到无限循环,因为primes是无限的,而elem试图非常确定放弃前4不是一个要素。我们人类知道4不是primes的元素,但elem不是。{/ p>

有两种方法可供选择。我们可以编写elem的自定义版本,一旦找到大于我们要查找的元素,就会放弃:

sortedElem :: Ord a => a -> [a] -> Bool
sortedElem x (h:tl) = case x `compare` h of
                           LT -> False
                           EQ -> True
                           GT -> sortedElem x tl
sortedElem _ [] = False
-- or
sortedElem x = foldr (\h tl -> case x `compare` h of
                                    LT -> False
                                    EQ -> True
                                    GT -> tl
                     ) False

由于primes是一个排序列表,sortedElem现在总会给出正确答案:

partC = snd <$> filter ((`sortedElem` primes) . fst) (zip [1..] fib)

然而,存在性能问题,因为每次调用sortedElem都必须从primes开始,然后一直向下走直到它确定索引是否正确。这导致了第二种方式:

partC = go primeDiffs fib
  where primeDiffs = zipWith (-) primes (1:primes)
     -- primeDiffs = [1, 1, 2, 2, 4, 2, 4, 2, 4, 6, ...]
     -- The distance from one prime (incl. 1) to the next
        go (step:steps) xs = x:go steps xs'
          where xs'@(x:_) = drop step xs
        go [] _ = [] -- unused here
     -- in real code you might pull this out into an atOrderedIndices :: [Int] -> [a] -> [a]

我们将索引列表(primes)转换为偏移列表,每个偏移量构建在下一个偏移列表中,我们将其称为primeDiffs。然后我们定义go来获取这样的偏移列表并从另一个列表中提取元素。它首先drop跳过的元素,然后在构建列表的其余部分之前将顶部元素放入结果中。在-O2下,在我的计算机上,此版本的速度是找到partC !! 5000时另一个版本的两倍。