无限的自引用列表

时间:2016-12-16 11:23:44

标签: haskell sequence infinite self-reference recursive-datastructures

问题

我正在尝试从Dragon Curve实现修改后的AoC Day 16作为Haskell中的无限列表。

该列表由TrueFalse组成。我们从一些列表s0开始:

  • s1 = s0 ++ [False] ++ (map not . reverse) s0
  • s2 = s1 ++ [False] ++ (map not . reverse) s1
  • s3 = s2 ++ [False] ++ (map not . reverse) s2

一般

sn = s(n-1) ++ [0] ++ (map not . reverse) s(n-1) 
   = s0 ++ [0] ++ (f s0) ++ [0] ++ (f (s0 ++ [0] ++ (f s0))) ++ ...
      where f = (map not . reverse)

尝试实施

我可以使用sn函数轻松获得iterate

modifiedDragonCurve :: [Bool] -> Int -> [Bool]
modifiedDragonCurve s n = (iterate f s)!!n
    where f s     = s ++ [False] ++ (map not . reverse) s 

这给了我一个列表[s0, s1, s2, ...]。但是,由于s(n-1)sn的前缀,因此可以将其构建为无限列表,但我无法弄清楚如何处理它。

我认为我需要一些东西
modifiedDragonCurve :: [Bool] -> [Bool]
modifiedDragonCurve s = s ++ [False] ++ (map not . reverse) listSoFar

但无法弄清楚如何引用已生成的列表(listSoFar)。

任何建议都将不胜感激。

4 个答案:

答案 0 :(得分:5)

我也在解决AoC问题的同时自己玩这个。我找到了一个不需要反向的卓越解决方案,因此比这里列出的其他解决方案更加内存友好和快速。它也很漂亮!龙曲线本身是一个很好的短双线:

merge (x:xs) ys = x:merge ys xs
dragon = merge (cycle [False, True]) dragon

它可以扩展为使用“种子”,因为AoC问题只需要在种子和真龙曲线的位之间交替:

infinite bs = go bs (map not (reverse bs)) dragon where
    go bs bs' (d:ds) = bs ++ [d] ++ go bs' bs ds

(这确实会调用reverse一次 - 但与其他解决方案不同的是,它只在一大堆关于输入大小的数据上调用一次,而不是在与该部分一样大的数据块上重复调用你消费的清单。)一些时间来证明我的主张;用于生成具有空种子的2 ^ 25个元素的所有版本,使用ghc -O2编译,并使用/usr/bin/time计时。

自由泳的解决方案需要11.64s,最多1.8Gb驻留 David Fletcher的解决方案需要10.71s,最大2Gb驻留 luqui的解决方案需要9.93秒,最大居民约1GB 我的解决方案需要8.87秒,最大居民约760MB

完整的测试程序

main = mapM_ print . take (2^25) . dragon $ []

dragon依次替换为每个实现。精心设计的消费者可以进一步降低内存使用率:到目前为止,我对第二个星问题的最佳解决方案是以5Mb实际驻留时间运行(即包括从OS分配的多代GHC空间和其他RTS开销的所有空间GHC) ),60Kb GHC报告的驻留时间(即只有尚未GC的对象使用的空间,无论GHC从操作系统分配了多少空间)。

但是对于原始速度,你无法击败Bool的未装箱的可变矢量:同事报告说他的程序运行时间为0.2秒,使用大约35Mb内存来存储完整的扩展(但不是无限!)矢量。

答案 1 :(得分:2)

这是一种方式。我们列出的不是s0,s1等,而是每一步只有新的部分,然后我们可以将它们连在一起。

dragonCurve :: [Bool]
dragonCurve = concatMap f [0..]
  where
    f n = False : (map not . reverse) (take (2^n-1) dragonCurve)

(这假定s0 = []。如果它可能是其他东西你将不得不修改长度计算。)

我无法想到一种既可以自我指导又不处理前缀长度的好方法。这是一个非自引用解决方案,仍然使用制作非重叠部分列表的想法。

dragonCurve' :: [Bool]
dragonCurve' = concat (unfoldr f [])
  where
    f soFar = Just (part, soFar ++ part)
      where
        part = False : (map not . reverse) soFar

答案 2 :(得分:2)

你完全是书呆子用这个来嘲笑我。它不是一个自我引用的列表,但我确实设法提出了一个“无用的”#34;解决方案 - 我们不会丢弃或忘记我们计算过的任何事情。

dragon :: [Bool] -> [Bool]
dragon = \s -> s ++ gen [] s
    where
    inv = map not . reverse
    gen a b =
        let b' = inv b
            r = b' ++ a
        in False:r ++ gen r (False:r)

gen a b接受当前序列的所有数据作为输入,使得当前序列为inv a ++ b。然后我们在r中生成余数,输出它并递归地继续生成余数。我接受a倒置,因为我需要做的就是在每个步骤(甚至不检查b')前加a,而且我们不需要反转超过DList我们必须。

作为nerdsniped我调查了很多其他数据结构,想象链接列表可能不是最适合这个问题,包括Data.Sequence <?php /* Attempt MySQL server connection. Assuming you are running MySQL server with default setting (user 'root' with no password) */ $link = mysqli_connect("localhost", "***", "***", "***"); // Check connection if($link === false){ die("ERROR: Could not connect. " . mysqli_connect_error()); } // Escape user inputs for security $cname = mysqli_real_escape_string($link, $_POST['cname']); $name = mysqli_real_escape_string($link, $_POST['name']); $contact = mysqli_real_escape_string($link, $_POST['contact']); $reason = mysqli_real_escape_string($link, $_POST['reason']); // attempt insert query execution $sql = "INSERT INTO keys (cname, name, contact, reason) VALUES ('$cname', '$name', '$contact', '$reason')"; if(mysqli_query($link, $sql)){ echo "<script>alert('User added!')</script>"; echo "<script>window.open('index.php','_self')</script>"; } else{ echo "ERROR: Could not able to execute $sql. " . mysqli_error($link); } // close connection mysqli_close($link); ?> (手指树),自由monoid(它应该擅长被逆转),以及取消逆转的自定义树。令我惊讶的是,列表仍然表现最好的所有这些,我仍然对此感到困惑。万一你好奇,here is my code

答案 3 :(得分:1)

例如:

dragon s0 = s0 ++ concatMap ((False :) . inv) seq
  where
    inv = map not . reverse
    seq = iterate (\s -> s ++ False : inv s) s0