【问题标题】:Proving that concatenating two increasing lists produces an increasing list证明连接两个递增列表会产生递增列表
【发布时间】:2018-04-05 21:30:36
【问题描述】:

让我们考虑一个谓词,表明列表中的元素是按递增顺序排列的(为简单起见,我们只处理非空列表):

mutual
  data Increasing : List a -> Type where
    SingleIncreasing  : (x : a) -> Increasing [x]
    RecIncreasing     : Ord a => (x : a) ->
                                 (rest : Increasing xs) ->
                                 (let prf = increasingIsNonEmpty rest
                                  in x <= head xs = True) ->
                                 Increasing (x :: xs)

  %name Increasing xsi, ysi, zsi

  increasingIsNonEmpty : Increasing xs -> NonEmpty xs
  increasingIsNonEmpty (SingleIncreasing y) = IsNonEmpty
  increasingIsNonEmpty (RecIncreasing x rest prf) = IsNonEmpty

现在让我们试着用这个谓词写一些有用的引理。假设第一个列表的最后一个元素不大于第二个列表的第一个元素,让我们首先展示连接两个递增列表会产生一个递增列表。这个引理的类型是:

appendIncreasing : Ord a => {xs : List a} ->
                            (xsi : Increasing xs) ->
                            (ysi : Increasing ys) ->
                            {auto leq : let xprf = increasingIsNonEmpty xsi
                                            yprf = increasingIsNonEmpty ysi
                                        in last xs <= head ys = True} ->
                            Increasing (xs ++ ys)

现在让我们尝试实现它!一种合理的方法似乎是在xsi 上拆分大小写。 xsi 是单个元素的基本情况很简单:

appendIncreasing {leq} (SingleIncreasing x) ysi = RecIncreasing x ysi leq

另一种情况更复杂。给定

appendIncreasing {leq} (RecIncreasing x rest prf) ysi = ?wut

通过依赖leq,然后使用prfx 前面添加restysi 的结果,递归地证明这一点似乎是合理的。此时leq实际上是last (x :: xs) &lt;= head ys = True的证明,而对appendIncreasing的递归调用需要有last xs &lt;= head ys = True的证明。我没有看到直接证明前者暗示后者的好方法,所以让我们回过头来重写并首先编写一个引理,表明列表的最后一个元素不会通过添加到前面来改变:

lastIsLast : (x : a) -> (xs : List a) -> {auto ok : NonEmpty xs} -> last xs = last (x :: xs)
lastIsLast x' [x] = Refl
lastIsLast x' (x :: y :: xs) = lastIsLast x' (y :: xs)

现在我希望能够写作

appendIncreasing {xs = x :: xs} {leq} (RecIncreasing x rest prf) ysi =
  let rest' = appendIncreasing {leq = rewrite lastIsLast x xs in leq} rest ysi
  in ?wut

但我失败了:

When checking right hand side of appendIncreasing with expected type
        Increasing ((x :: xs) ++ ys)

When checking argument leq to Sort.appendIncreasing:
        rewriting last xs to last (x :: xs) did not change type last xs <= head ys = True

我该如何解决这个问题?

而且,也许我的证明设计不是最理想的。有没有办法以更有用的方式表达这个谓词?

【问题讨论】:

    标签: idris


    【解决方案1】:

    如果rewrite 找不到正确的谓词,请尝试使用replace 显式。

    appendIncreasing {a} {xs = x :: xs} {ys} (RecIncreasing x rest prf) ysi leq = 
      let rekPrf = replace (sym $ lastIsLast x xs) leq
                   {P=\T => (T <= (head ys {ok=increasingIsNonEmpty ysi})) = True} in
      let rek = appendIncreasing rest ysi rekPrf in
      let appPrf = headIsHead xs ys {q = increasingIsNonEmpty rek} in
      let extPrf = replace appPrf prf {P=\T => x <= T = True} in
      RecIncreasing x rek extPrf
    

    headIsHead : (xs : List a) -> (ys : List a) ->
                 {auto p : NonEmpty xs} -> {auto q : NonEmpty (xs ++ ys)} ->
                 head xs = head (xs ++ ys)
    headIsHead (x :: xs) ys = Refl
    

    一些建议:

    1. 使用Data.So x 代替x = True,生成运行时函数 更容易编写。
    2. Ord a从构造函数提升到类型,使其 更清楚使用哪种排序(并且您不必匹配 {a} appendIncreasing,我猜)。
    3. 别忘了你可以 匹配构造函数中的变量,所以不要重复 Increasing xsNonEmpty xs,请使用Increasing (x :: xs)

    导致:

    data Increasing : Ord a -> List a -> Type where
      SingleIncreasing : (x : a) -> Increasing ord [x]
      RecIncreasing    : (x : a) -> Increasing ord (y :: ys) ->
                                    So (x <= y) ->
                                    Increasing ord (x :: y :: ys)
    
    appendIncreasing : {ord : Ord a} ->
                       Increasing ord (x :: xs) -> Increasing ord (y :: ys) ->
                       So (last (x :: xs) <= y) ->
                       Increasing ord ((x :: xs) ++ (y :: ys))
    

    应该使证明事情变得容易得多,尤其是如果您想包含空列表。

    【讨论】:

    • 感谢您的建议!同时,我想我已经弄清楚为什么rewrite 对我不起作用,以及为什么我得到其他奇怪错误的来源 - 简而言之,类型检查器没有统一来自不同来源的Ord a 约束出于某种原因,因此我遇到了有趣的类型不匹配(:set showimplicits 确实有助于调试)。除了您的建议之外,我还用一个应该是预购的函数 a -&gt; a -&gt; Bool 替换了 Ord a,并使 Increasing 的类型依赖于它,这似乎产生了更简洁的代码,而且所需的引理更少.
    • 我从未听说过“让所有证明都隐含参数,这样它们就可以被删除。”。 AFAIK:隐式隐式参数(那些没有在大括号中给出但通过小写隐式隐含的参数)被标记为擦除,但除非被询问,否则显式隐式参数(在大括号中给出)永远不会被擦除。显式参数也是如此。您可以使用.{x : tp}.(x : tp) 将任何显式给定的参数标记为已删除。复杂的证明论证通常是明确的。无论如何,我不认为你可以在这里删除它们,因为它们中的证明被用于结果。
    • @HTNW 你是对的,谢谢! (但至少在这里 Idris 可以擦除证明:--warnreach.(So (x &lt;= y)) 编译时不会发出警告。证明只需要证明部分。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    • 2018-02-28
    • 2018-04-22
    • 1970-01-01
    • 1970-01-01
    • 2022-12-11
    • 1970-01-01
    相关资源
    最近更新 更多