这是一堂课 我们应该编写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 ..]
答案 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
时另一个版本的两倍。