Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/ssh/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 我如何在不重复自己的情况下使这个算法更懒惰?_Haskell_Lazy Evaluation_Dry - Fatal编程技术网

Haskell 我如何在不重复自己的情况下使这个算法更懒惰?

Haskell 我如何在不重复自己的情况下使这个算法更懒惰?,haskell,lazy-evaluation,dry,Haskell,Lazy Evaluation,Dry,(灵感来源于我对问题的回答。) 考虑以下代码(它应该找到小于或等于给定输入的最大元素): 这不是很懒。一旦输入了GT案例,我们可以确定最终返回值将是Just某物,而不是Nothing,但是Just直到最后才可用。我想让它更懒,这样只要输入GT案例,就可以使用Just。我的测试用例是,我想要Data.Maybe.isJust$closestless5(节点3()叶未定义)计算为True,而不是见底。以下是我可以想到的一种方法: data TreeMap v = Leaf | Node Intege

(灵感来源于我对问题的回答。)

考虑以下代码(它应该找到小于或等于给定输入的最大元素):

这不是很懒。一旦输入了
GT
案例,我们可以确定最终返回值将是
Just
某物,而不是
Nothing
,但是
Just
直到最后才可用。我想让它更懒,这样只要输入
GT
案例,
就可以使用
Just
。我的测试用例是,我想要
Data.Maybe.isJust$closestless5(节点3()叶未定义)
计算为
True
,而不是见底。以下是我可以想到的一种方法:

data TreeMap v = Leaf | Node Integer v (TreeMap v) (TreeMap v) deriving (Show, Read, Eq, Ord)

closestLess :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess _ Leaf = Nothing
closestLess i (Node k v l r) = case i `compare` k of
  LT -> closestLess i l
  EQ -> Just (k, v)
  GT -> Just (precise (k, v) r)
  where
    precise :: (Integer, v) -> TreeMap v -> (Integer, v)
    precise closestSoFar Leaf = closestSoFar
    precise closestSoFar (Node k v l r) = case i `compare` k of
      LT -> precise closestSoFar l
      EQ -> (k, v)
      GT -> precise (k, v) r
然而,我现在重复我自己的话:核心逻辑现在在
closestLess
precise
中。我怎么能写得这么懒却不重复我自己的话呢?

怎么样

GT -> let Just v = precise (Just (k,v) r) in Just v

从我的非惰性实现开始,我首先重构了
precise
以接收
Just
作为参数,并相应地概括了它的类型:

data TreeMap v = Leaf | Node Integer v (TreeMap v) (TreeMap v) deriving (Show, Read, Eq, Ord)

closestLess :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess i = precise Just Nothing where
  precise :: ((Integer, v) -> t) -> t -> TreeMap v -> t
  precise _ closestSoFar Leaf = closestSoFar
  precise wrap closestSoFar (Node k v l r) = case i `compare` k of
    LT -> precise wrap closestSoFar l
    EQ -> wrap (k, v)
    GT -> precise wrap (wrap (k, v)) r
然后,我将其更改为提前执行
wrap
,并在
GT
案例中使用
id
调用自己:

data TreeMap v = Leaf | Node Integer v (TreeMap v) (TreeMap v) deriving (Show, Read, Eq, Ord)

closestLess :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess i = precise Just Nothing where
  precise :: ((Integer, v) -> t) -> t -> TreeMap v -> t
  precise _ closestSoFar Leaf = closestSoFar
  precise wrap closestSoFar (Node k v l r) = case i `compare` k of
    LT -> precise wrap closestSoFar l
    EQ -> wrap (k, v)
    GT -> wrap (precise id (k, v) r)

这仍然像以前一样有效,除了增加了懒惰的好处。

我认为您自己回答的CPS版本是最好的,但为了完整性,这里还有一些想法。(编辑:布尔的答案现在是最有效的。)

第一个想法是去掉“
closestSoFar
”累加器,而是让
GT
案例处理选择比参数最小的最右边的值的所有逻辑。在这种形式下,
GT
案例可以直接返回一个
Just

closestLess1::Integer->TreeMap v->Maybe(Integer,v)
closestLess1 uu叶=无
闭式1 i(节点k v l r)=
案例一`比较` k
LT->无门1 i l
EQ->Just(k,v)
GT->Just(从可能(k,v)(最接近的1 i r))
这更简单,但当您遇到大量
GT
案例时,会占用堆栈上更多的空间。从技术上讲,您甚至可以在累加器形式中使用
fromMaybe
(即,替换luqui答案中隐含的
fromJust
),但这将是一个冗余的、无法访问的分支

另一个想法是算法实际上有两个“阶段”,一个在你点击
GT
之前,一个在点击之后,所以你用一个布尔值来表示这两个阶段,并使用依赖类型来编码不变量,即在第二个阶段中总是会有结果

datasbool(b::Bool)其中
STrue::SBool'True
SFalse::SBool'False
除非(b::Bool)a where,否则类型族可以是
可能是除非“真a=a”
可能是除非“假a=可能是a”
ret::SBool b->a->maybebebea
ret SFalse=刚刚
ret STrue=id
closestLess2::Integer->TreeMap v->Maybe(Integer,v)
closestLess2 i=精确的S无任何
精确::SBool b->maybeeunclerb(整数,v)->树映射v->maybeeunclerb(整数,v)
精确的uuostofar叶=closesstofar叶
精确b闭合点(节点k v l r)=案例i`compare`k of
LT->精确b关闭至左侧
等式->返回b(k,v)
GT->ret b(精密串(k,v)r)

您可以利用类型系统,而不是使用显式包装器。请注意,将
Maybe
用作第一个代码段的
precise
版本:

precise :: Maybe (Integer, v) -> TreeMap v -> Maybe (Integer, v)
precise closestSoFar Leaf = closestSoFar
precise closestSoFar (Node k v l r) = case i `compare` k of
  LT -> precise closestSoFar l
  EQ -> Just (k, v)
  GT -> precise (Just (k, v)) r
与第二个代码段中的
precise
版本几乎完全相同,没有
Maybe
,可以在
Identity
functor中编写为:

precise :: Identity (Integer, v) -> TreeMap v -> Identity (Integer, v)
precise closestSoFar Leaf = closestSoFar
precise closestSoFar (Node k v l r) = case i `compare` k of
  LT -> precise closestSoFar l
  EQ -> Identity (k, v)
  GT -> precise (Identity (k, v)) r
这些可以统一为
应用程序中的多态版本

precise :: (Applicative f) => f (Integer, v) -> TreeMap v -> f (Integer, v)
precise closestSoFar Leaf = closestSoFar
precise closestSoFar (Node k v l r) = case i `compare` k of
  LT -> precise closestSoFar l
  EQ -> pure (k, v)
  GT -> precise (pure (k, v)) r
就其本身而言,这并没有多大作用,但是如果我们知道
GT
分支总是会返回一个值,那么我们可以强制它在
Identity
函子中运行,而不管起始函子是什么。也就是说,我们可以从
Maybe
函子开始,但递归到
GT
分支中的
Identity
函子:

closestLess :: Integer -> TreeMap v -> Maybe (Integer, v)
closestLess i = precise Nothing
  where
    precise :: (Applicative t) => t (Integer, v) -> TreeMap v -> t (Integer, v)
    precise closestSoFar Leaf = closestSoFar
    precise closestSoFar (Node k v l r) = case i `compare` k of
      LT -> precise closestSoFar l
      EQ -> pure (k, v)
      GT -> pure . runIdentity $ precise (Identity (k, v)) r
这适用于您的测试用例:

> isJust $ closestLess 5 (Node 3 () Leaf undefined)
True
这是多态递归的一个很好的例子

从性能角度来看,这种方法的另一个优点是,
-ddump siml
显示没有包装器或字典。在类型级别上,使用两个函子的专用函数将其全部擦除:

closestLess
  = \ @ v i eta ->
      letrec {
        $sprecise
        $sprecise
          = \ @ v1 closestSoFar ds ->
              case ds of {
                Leaf -> closestSoFar;
                Node k v2 l r ->
                  case compareInteger i k of {
                    LT -> $sprecise closestSoFar l;
                    EQ -> (k, v2) `cast` <Co:5>;
                    GT -> $sprecise ((k, v2) `cast` <Co:5>) r
                  }
              }; } in
      letrec {
        $sprecise1
        $sprecise1
          = \ @ v1 closestSoFar ds ->
              case ds of {
                Leaf -> closestSoFar;
                Node k v2 l r ->
                  case compareInteger i k of {
                    LT -> $sprecise1 closestSoFar l;
                    EQ -> Just (k, v2);
                    GT -> Just (($sprecise ((k, v2) `cast` <Co:5>) r) `cast` <Co:4>)
                  }
              }; } in
      $sprecise1 Nothing eta
无门
=\@v i eta->
莱特雷克{
$sprecise
$sprecise
=\@v1关闭日期ds->
案件{
叶->近叶;
节点k v2 l r->
案例比较者i k{
LT->$s截止到l的精确关闭;
等式->(k,v2)`cast`;
GT->$sprecise((k,v2)`cast`)r
}
}}in
莱特雷克{
$sprecise1
$sprecise1
=\@v1关闭日期ds->
案件{
叶->近叶;
节点k v2 l r->
案例比较者i k{
LT->$sprecise1关闭至l;
EQ->Just(k,v2);
GT->Just($sprecise((k,v2)`cast`)r)`cast`)
}
}}in
$sprecise1无任何预计到达时间

我们不仅总是知道
只是
,在它第一次被发现之后,我们也总是什么都不知道,直到那时。这实际上是两种不同的“逻辑”

我们先往左走,明确点:

data TreeMap v = Leaf | Node Integer v (TreeMap v) (TreeMap v) 
                 deriving (Show, Read, Eq, Ord)

closestLess :: Integer 
            -> TreeMap v 
            -> Maybe (Integer, v)
closestLess i = goLeft 
  where
  goLeft :: TreeMap v -> Maybe (Integer, v)
  goLeft n@(Node k v l _) = case i `compare` k of
          LT -> goLeft l
          _  -> Just (precise (k, v) n)
  goLeft Leaf = Nothing

  -- no more maybe if we're here
  precise :: (Integer, v) -> TreeMap v -> (Integer, v)
  precise closestSoFar Leaf           = closestSoFar
  precise closestSoFar (Node k v l r) = case i `compare` k of
        LT -> precise closestSoFar l
        EQ -> (k, v)
        GT -> precise (k, v) r

价格是我们最多重复一步最多一次。

因为这是一个不完整的模式匹配。即使我的函数是一个整体,但我不喜欢它的一部分是局部的。所以你说“我们肯定知道”仍然有些疑问。也许是
data TreeMap v = Leaf | Node Integer v (TreeMap v) (TreeMap v) 
                 deriving (Show, Read, Eq, Ord)

closestLess :: Integer 
            -> TreeMap v 
            -> Maybe (Integer, v)
closestLess i = goLeft 
  where
  goLeft :: TreeMap v -> Maybe (Integer, v)
  goLeft n@(Node k v l _) = case i `compare` k of
          LT -> goLeft l
          _  -> Just (precise (k, v) n)
  goLeft Leaf = Nothing

  -- no more maybe if we're here
  precise :: (Integer, v) -> TreeMap v -> (Integer, v)
  precise closestSoFar Leaf           = closestSoFar
  precise closestSoFar (Node k v l r) = case i `compare` k of
        LT -> precise closestSoFar l
        EQ -> (k, v)
        GT -> precise (k, v) r