Optimization 总体性和对流中元素的搜索
我想要一个用于大小有界类型流的Optimization 总体性和对流中元素的搜索,optimization,stream,idris,lazy-sequences,finite-group-theory,Optimization,Stream,Idris,Lazy Sequences,Finite Group Theory,我想要一个用于大小有界类型流的find函数,它类似于用于列表和向量的find函数 total find : MaxBound a => (a -> Bool) -> Stream a -> Maybe a 挑战在于如何做到: 全部 消耗的空间不超过常量log_2 N,其中N是编码最大a所需的位数 在编译时检查不超过一分钟 不增加运行时成本 一般来说,流的总find实现听起来很荒谬。流是无限的,一个const False谓词将使搜索永远进行下去。处理这种一般情况的一个好方
find
函数,它类似于用于列表和向量的find函数
total
find : MaxBound a => (a -> Bool) -> Stream a -> Maybe a
挑战在于如何做到:
a
所需的位数const False
谓词将使搜索永远进行下去。处理这种一般情况的一个好方法是无限燃料技术
data Fuel = Dry | More (Lazy Fuel)
partial
forever : Fuel
forever = More forever
total
find : Fuel -> (a -> Bool) -> Stream a -> Maybe a
find Dry _ _ = Nothing
find (More fuel) f (value :: xs) = if f value
then Just value
else find fuel f xs
这对我的用例很有效,但我想知道在某些特殊情况下,是否可以不使用永久而说服总体检查器。否则,有些人可能会经历一段无聊的生活,等待find ever?谓词始终返回false(迭代sz)
完成
考虑一种特殊情况,a
是Bits32
find32 : (Bits32 -> Bool) -> Stream Bits32 -> Maybe Bits32
find32 f (value :: xs) = if f value then Just value else find32 f xs
两个问题:它不是总的,并且它不可能返回Nothing
,即使有有限数量的位32
居民要尝试。也许我可以使用take(pow 2 32)
建立一个列表,然后使用List的find…呃,等等…单是列表就需要占用GBs的空间
原则上,这似乎并不难。有有限多的居民可以尝试,现代计算机可以在几秒钟内迭代所有32位排列。是否有办法让总体检查器验证(流位32)$iterate(+1)0
最终循环回0
,一旦它断言所有元素都已尝试过,因为(+1)
是纯的
这是一个开始,尽管我不确定如何填补这些漏洞,如何专门化找到足够的,使之成为完整的。也许界面会有帮助
total
IsCyclic : (init : a) -> (succ : a -> a) -> Type
data FinStream : Type -> Type where
MkFinStream : (init : a) ->
(succ : a -> a) ->
{prf : IsCyclic init succ} ->
FinStream a
partial
find : Eq a => (a -> Bool) -> FinStream a -> Maybe a
find pred (MkFinStream {prf} init succ) = if pred init
then Just init
else find' (succ init)
where
partial
find' : a -> Maybe a
find' x = if x == init
then Nothing
else
if pred x
then Just x
else find' (succ x)
total
all32bits : FinStream Bits32
all32bits = MkFinStream 0 (+1) {prf=?prf}
有没有办法告诉全集检查器使用无限燃料来验证对特定流的搜索是否为全集?让我们定义循环序列的含义:
%default total
iter : (n : Nat) -> (a -> a) -> (a -> a)
iter Z f = id
iter (S k) f = f . iter k f
isCyclic : (init : a) -> (next : a -> a) -> Type
isCyclic init next = DPair (Nat, Nat) $ \(m, n) => (m `LT` n, iter m next init = iter n next init)
上述情况意味着我们的情况可以描述如下:
-- x0 -> x1 -> ... -> xm -> ... -> x(n-1) --
-- ^ |
-- |---------------------
其中m
严格小于n
(但是m
可以等于零)n
是一些步骤,在这些步骤之后,我们将获得之前遇到的序列元素
data FinStream : Type -> Type where
MkFinStream : (init : a) ->
(next : a -> a) ->
{prf : isCyclic init next} ->
FinStream a
接下来,让我们定义一个helper函数,它使用一个名为fuel
的上限来打破循环:
findLimited : (p : a -> Bool) -> (next : a -> a) -> (init : a) -> (fuel : Nat) -> Maybe a
findLimited p next x Z = Nothing
findLimited p next x (S k) = if p x then Just x
else findLimited pred next (next x) k
现在可以这样定义find
:
find : (a -> Bool) -> FinStream a -> Maybe a
find p (MkFinStream init next {prf = ((_,n) ** _)}) =
findLimited p next init n
以下是一些测试:
-- I don't have patience to wait until all32bits typechecks
all8bits : FinStream Bits8
all8bits = MkFinStream 0 (+1) {prf=((0, 256) ** (LTESucc LTEZero, Refl))}
exampleNothing : Maybe Bits8
exampleNothing = find (const False) all8bits -- Nothing
exampleChosenByFairDiceRoll : Maybe Bits8
exampleChosenByFairDiceRoll = find ((==) 4) all8bits -- Just 4
exampleLast : Maybe Bits8
exampleLast = find ((==) 255) all8bits -- Just 255
isCyclic
和find
是否可以在不依赖线性空间Nat
s的情况下实现?我甚至没有足够的耐心让所有16位
在我的机器上完成,我认为Nat
是罪魁祸首。在我的机器上,迭代所有无符号int
s的未优化C程序需要10秒多一点的时间,我的目标是在恒定空间消耗的情况下获得与之相当的性能。你是对的,这是一个长期存在的问题。二进制数可以减轻痛苦。github上有一个项目,旨在将Coq的二进制数移植到IdrisAre,这些二进制数是不是一个未优化的归纳定义?我想这会有帮助,但是可以使用优化的任意精度无符号整数来实现吗?(无可否认,这两种解决方案都会消耗$\log_2 n$而不是常量空间。)无论如何,谢谢您的回答。看到一种实现isCyclic
@JakeMitchell的方法很有启发性,是的,这只是一个归纳定义,回购协议就在这里,我们有一些计划从位n
到这些,但还没有什么具体的,欢迎您在问题/PR部分提出一些建议:)