Как реализовать алгоритм зайца и черепахи Флойда в Agda?
Я хочу перевести следующий код Haskell в Agda:
import Control.Arrow (first)
import Control.Monad (join)
safeTail :: [a] -> [a]
safeTail [] = []
safeTail (_:xs) = xs
floyd :: [a] -> [a] -> ([a], [a])
floyd xs [] = ([], xs)
floyd (x:xs) (_:ys) = first (x:) $ floyd xs (safeTail ys)
split :: [a] -> ([a], [a])
split = join floyd
это позволяет эффективно разделить список на два:
split [1,2,3,4,5] = ([1,2,3], [4,5])
split [1,2,3,4,5,6] = ([1,2,3], [4,5,6])
Итак, я попытался преобразовать этот код в Agda:
floyd : {A : Set} → List A → List A → List A × List A
floyd xs [] = ([] , xs)
floyd (x :: xs) (_ :: ys) = first (x ::_) (floyd xs (safeTail ys))
единственная проблема в том, что Agda жалуется, что я пропускаю случай для floyd [] (y :: ys)
. Однако этот случай никогда не должен возникать. Как я могу доказать Agda, что этот случай никогда не должен возникать?
вот наглядный пример того, как этот алгоритм работает:
+-------------+-------------+
| Tortoise | Hare |
+-------------+-------------+
| ^ 1 2 3 4 5 | ^ 1 2 3 4 5 |
| 1 ^ 2 3 4 5 | 1 2 ^ 3 4 5 |
| 1 2 ^ 3 4 5 | 1 2 3 4 ^ 5 |
| 1 2 3 ^ 4 5 | 1 2 3 4 5 ^ |
+-------------+-------------+
вот еще один пример:
+---------------+---------------+
| Tortoise | Hare |
+---------------+---------------+
| ^ 1 2 3 4 5 6 | ^ 1 2 3 4 5 6 |
| 1 ^ 2 3 4 5 6 | 1 2 ^ 3 4 5 6 |
| 1 2 ^ 3 4 5 6 | 1 2 3 4 ^ 5 6 |
| 1 2 3 ^ 4 5 6 | 1 2 3 4 5 6 ^ |
+---------------+---------------+
когда заяц (второй аргумент floyd
) достигает конца списка, черепаха (первый аргумент floyd
) достигает середины списка. Таким образом, используя два указателя (второй движется вдвое быстрее первого), мы можем эффективно разделить список на два.
1 ответов
то же самое как Тван Ван Laarhoven предлагает в комментариях, но с Vec
s. Его версия, вероятно, лучше.
open import Function
open import Data.Nat.Base
open import Data.Product renaming (map to pmap)
open import Data.List.Base
open import Data.Vec hiding (split)
≤-step : ∀ {m n} -> m ≤ n -> m ≤ suc n
≤-step z≤n = z≤n
≤-step (s≤s le) = s≤s (≤-step le)
≤-refl : ∀ {n} -> n ≤ n
≤-refl {0} = z≤n
≤-refl {suc n} = s≤s ≤-refl
floyd : ∀ {A : Set} {n m} -> m ≤ n -> Vec A n -> Vec A m -> List A × List A
floyd z≤n xs [] = [] , toList xs
floyd (s≤s z≤n) (x ∷ xs) (y ∷ []) = x ∷ [] , toList xs
floyd (s≤s (s≤s le)) (x ∷ xs) (y ∷ z ∷ ys) = pmap (x ∷_) id (floyd (≤-step le) xs ys)
split : ∀ {A : Set} -> List A -> List A × List A
split xs = floyd ≤-refl (fromList xs) (fromList xs)
open import Relation.Binary.PropositionalEquality
test₁ : split (1 ∷ 2 ∷ 3 ∷ 4 ∷ 5 ∷ []) ≡ (1 ∷ 2 ∷ 3 ∷ [] , 4 ∷ 5 ∷ [])
test₁ = refl
test₂ : split (1 ∷ 2 ∷ 3 ∷ 4 ∷ 5 ∷ 6 ∷ []) ≡ (1 ∷ 2 ∷ 3 ∷ [] , 4 ∷ 5 ∷ 6 ∷ [])
test₂ = refl
кроме того, функции, такие как ≤-refl
и ≤-step
где-то в стандартной библиотеке, но мне было лень искать.
вот глупость я как чтобы делать:
open import Function
open import Data.Nat.Base
open import Data.Product renaming (map to pmap)
open import Data.List.Base
open import Data.Vec hiding (split)
floyd : ∀ {A : Set}
-> (k : ℕ -> ℕ)
-> (∀ {n} -> Vec A (k (suc n)) -> Vec A (suc (k n)))
-> ∀ n
-> Vec A (k n)
-> List A × List A
floyd k u 0 xs = [] , toList xs
floyd k u 1 xs with u xs
... | x ∷ xs' = x ∷ [] , toList xs'
floyd k u (suc (suc n)) xs with u xs
... | x ∷ xs' = pmap (x ∷_) id (floyd (k ∘ suc) u n xs')
split : ∀ {A : Set} -> List A -> List A × List A
split xs = floyd id id (length xs) (fromList xs)
open import Relation.Binary.PropositionalEquality
test₁ : split (1 ∷ 2 ∷ 3 ∷ 4 ∷ 5 ∷ []) ≡ (1 ∷ 2 ∷ 3 ∷ [] , 4 ∷ 5 ∷ [])
test₁ = refl
test₂ : split (1 ∷ 2 ∷ 3 ∷ 4 ∷ 5 ∷ 6 ∷ []) ≡ (1 ∷ 2 ∷ 3 ∷ [] , 4 ∷ 5 ∷ 6 ∷ [])
test₂ = refl
это частично основано на Бенджамин Ходжсон предложение в комментарии ниже.