Тотальность и поиск элементов в потоках
Я хочу find
функция для потоков ограниченных по размеру типов, которая аналогична функциям поиска для списков и Vects.
total
find : MaxBound a => (a -> Bool) -> Stream a -> Maybe a
Задача состоит в том, чтобы сделать это:
- быть полным
- потреблять не более
константыlog_2 N пробел, где N - количество битов, необходимых для кодирования наибольшегоa
, - не более минуты, чтобы проверить во время компиляции
- не накладывать затраты времени выполнения
Вообще, полная реализация find для Streams звучит абсурдно. Потоки бесконечны и предикат 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
Это хорошо работает для моего варианта использования, но мне интересно, можно ли в определенных специализированных случаях проверить целостность без использования forever
, В противном случае, кто-то может страдать от скучной жизни в ожидании find forever ?predicateWhichHappensToAlwaysReturnFalse (iterate S Z)
заканчивать.
Рассмотрим особый случай, когда a
является Bits32
,
find32 : (Bits32 -> Bool) -> Stream Bits32 -> Maybe Bits32
find32 f (value :: xs) = if f value then Just value else find32 f xs
Две проблемы: это не тотально и не может вернуться Nothing
хотя есть конечное число Bits32
жители попробовать. Может быть, я мог бы использовать take (pow 2 32)
чтобы создать список, а затем использовать поиск списка... ну, подождите... один только список занял бы ГБ места.
В принципе это не кажется трудным. Конечно, есть много жителей, и современный компьютер может перебирать все 32-битные перестановки за считанные секунды. Есть ли способ проверить целостность the (Stream Bits32) $ iterate (+1) 0
в конце концов возвращается к 0
и как только он утверждает, что все элементы были проверены с (+1)
чисто?
Вот начало, хотя я не уверен, как заполнить дыры и специализироваться find
достаточно, чтобы сделать его полным. Может быть, интерфейс поможет?
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}
Есть ли способ сказать контролеру совокупности использовать бесконечное топливо, проверяя, что поиск по определенному потоку является полным?
1 ответ
Давайте определим, что означает цикличность последовательности:
%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
Далее, давайте определим вспомогательную функцию, которая использует верхнюю границу под названием 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