如何在Haskell中创建无限列表,其中新值将消耗所有先前的值

问题描述

如果我这样创建一个无限列表:

let t xs = xs ++ [sum(xs)]
let xs = [1,2] : map (t) xs
take 10 xs

我会得到以下结果:

[
[1,2],[1,2,3],3,6],6,12],12,24],24,48],48,96],96,192],192,384],384,768]
]

这与我要执行的操作非常接近。

此当前代码使用最后一个值定义下一个。但是,除了列表列表以外,我想知道一种制作无限列表的方法,该列表使用所有先前的值定义新列表。

所以输出将只是

[1,768,1536,...]

我已经定义了第一个元素[1]

我有一个获取新元素的规则,将所有先前的元素加总。 但是,我无法将其放入Haskell语法中以创建无限列表。

使用当前代码,我可以使用以下命令获取所需列表:

xs !! 10
> [1,1536]

但是,在我看来,有可能以更有效的方式做到这一点。

一些注释

我知道,对于这个特定示例,这是故意过分简化的,我们可以创建一个仅使用最后一个值来定义下一个的函数。

但是,我正在搜索是否有可能将所有先前的值读入一个无限列表定义中。

很抱歉,如果我使用的示例引起了一些混乱。

再举一个例子,仅读取最后一个值是不可能解决的:

isMultipleByList :: Integer -> [Integer] -> Bool
isMultipleByList _ [] = False
isMultipleByList v (x:xs) = if (mod v x == 0)
                        then True
                        else (isMultipleByList v xs)

nextNotMultipleLoop :: Integer -> Integer -> [Integer] -> Integer
nextNotMultipleLoop step v xs = if not (isMultipleByList v xs)
                        then v
                        else nextNotMultipleLoop step (v + step) xs

nextNotMultiple :: [Integer] -> Integer
nextNotMultiple xs  = if xs == [2]
                        then nextNotMultipleLoop 1 (maximum xs) xs
                        else nextNotMultipleLoop 2 (maximum xs) xs


addNextNotMultiple xs = xs ++ [nextNotMultiple xs] 
infinitePrimeList = [2] : map (addNextNotMultiple) infinitePrimeList
take 10 infinitePrimeList
[
[2,[2,5],5,7],7,11],11,13],13,17],17,19],19,23],23,29],29,31]
]

infinitePrimeList !! 10
[2,31,37]

解决方法

您可以这样认为:

  1. 您要创建一个以a开头的列表(称为[1,2]):
a = [1,2] ++ ???
  1. ...并具有此属性:a中的每个下一个元素都是a中所有先前元素的总和。这样你就可以写
scanl1 (+) a

并获得一个新列表,其中索引为n的任何元素都是列表n的{​​{1}}个头元素的总和。因此,它是a。您需要做的就是不带任何先机地获取所有元素:

[1,3,6 ...]

因此,您可以将tail (scanl1 (+) a) 定义为:

a

这种想法可以通过其元素应用于定义列表的其他类似问题。

,

如果我们已经有了最终结果,则可以很容易地计算出给定元素的先前元素列表,这是inits函数的简单应用。

假设我们已经已经最终结果xs,并使用它来计算xs本身:

import Data.List (inits)

main :: IO ()
main = do    
    let is = drop 2 $ inits xs
        xs = 1 : 2 : map sum is
    print $ take 10 xs

这将产生列表

[1,2,6,12,24,48,96,192,384]

注意:这比SergeyKuz1001的解决方案效率低,因为每次都会重新计算总和。)

,

unfoldr具有很好的灵活性,可以适应各种“从初始条件创建列表”问题,因此我认为值得一提。

在这种情况下,优雅程度有所降低,但展示了如何使用unfoldr

import Data.List

nextVal as = Just (s,as++[s]) 
  where s = sum as

initList = [1,2]

myList =initList ++ ( unfoldr nextVal initList)

main = putStrLn . show . (take 12) $ myList

屈服

[1,384,768,1536]

最后。


正如评论中指出的那样,使用unfoldr时应该三思。我在上面的编写方式中,该代码模仿了原始问题中的代码。但是,这意味着用as++[s]更新累加器,从而在每次迭代时构造一个新列表。在https://repl.it/languages/haskell处快速运行表明它变得非常占用内存且速度很慢。 (4.5秒访问myList中的第2000个元素

将交换器更新仅交换为a:as,速度提高了7倍。由于相同的列表可以在每个步骤中作为累加器重用,因此运行速度更快。但是,累加器列表现在是相反的,因此需要三思。对于谓词功能sum,这没有什么区别,但是如果列表的顺序很重要,则必须多加考虑。

,

您可以这样定义它:

xs = 1:2:iterate (*2) 3

例如:

Prelude> take 12 xs
[1,1536]
,

这是我的看法。我尝试不创建O(n)个额外列表。

explode ∷ Integral i ⇒ (i ->[a] -> a) -> [a] -> [a]
explode fn init = as where
                     as = init ++ [fn i as | i <- [l,l+1..]]
                     l = genericLength init

此便利功能确实会创建其他列表(按take)。希望它们可以被编译器优化。

explode' f = explode (\x as -> f $ take x as)

用法示例:

myList = explode' sum [1,2]

sum' 0 xs = 0
sum' n (x:xs) = x + sum' (n-1) xs
myList2 = explode sum' [1,2]

在我的测试中,这两个功能之间几乎没有性能差异。 explode'通常会稍微好一些。

,

solution中的@LudvigH非常清晰。但是,速度并不快。

我仍在研究基准测试以比较其他选项。

目前,这是我能找到的最佳解决方案:

-------------------------------------------------------------------------------------
-- # infinite sum of the previous using fuse
-------------------------------------------------------------------------------------

recursiveSum xs = [nextValue] ++ (recursiveSum (nextList)) where
      nextValue = sum(xs)
      nextList = xs ++ [nextValue]

initialSumValues = [1]
infiniteSumFuse =  initialSumValues  ++ recursiveSum initialSumValues

-------------------------------------------------------------------------------------
-- # infinite prime list using fuse
-------------------------------------------------------------------------------------

-- calculate the current value based in the current list
-- call the same function with the new combined value
recursivePrimeList xs = [nextValue] ++ (recursivePrimeList (nextList)) where
      nextValue = nextNonMultiple(xs)
      nextList = xs ++ [nextValue]

initialPrimes = [2]
infiniteFusePrimeList =  initialPrimes ++ recursivePrimeList initialPrimes

这种方法速度很快,并充分利用了许多内核。

也许有一些更快的解决方案,但我决定发布此信息,以分享到目前为止我在该主题上的最新进展。

,

通常,定义

EndRead

然后是xs = x1 : zipWith f xs (inits xs) ,依此类推。

Here's在计算素数的无限列表(将它们配对为

)的背景下使用xs == x1 : f x1 [] : f x2 [x1] : f x3 [x1,x2] : ....的一个示例
inits

(在ps = 2 : f p1 [p1] : f p2 [p1,p2] : f p3 [p1,p2,p3] : ... 的定义中)。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...