在Haskell树中找到最接近整数参数的键

时间:2019-12-14 15:44:10

标签: haskell functional-programming binary-search-tree

有很多解决方案,如何以命令式语言在二叉树中查找最接近的上下键,但是在像Haskell这样的纯函数样式中进行操作时,缺少相同的问题。我很好奇要学习在遇到两个最接近的键之前如何绕过二叉树。到目前为止,有一个功能和一些模式匹配:

data TreeMap v = Leaf | Node { pair::(Integer, v), l::TreeMap v, r::TreeMap v} deriving (Show, Read, Eq, Ord)

closestLess :: Integer -> TreeMap v -> (Integer, v)
closestLess i Leaf = error "Tree doesn't include any element"
closestLess i (Node pair tree_r tree_l)
        | i < fst pair = closestLess i tree_l
        | i == fst pair = closestLess i tree_r
        | otherwise = precise i pair tree_r 

我使用此函数来获取一个较低的键,但最接近Integer参数。我认为,除了实现“精确”之类的辅助功能外,没有什么必要,尽管我对它确切需要什么定义感到困惑。我的建议是将整数值节点置于“精确”右子树中,以找到最接近目标的任何键。因此,我将有一些技巧或假设,也适用于上下键。

1 个答案:

答案 0 :(得分:0)

这就是我要做的:

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 Nothing where
  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

有关此尝试与您尝试之间的区别的一些注意事项:

  • 您将记录语法用于Node构造函数。在总和类型上使用记录语法是一种不好的形式,因为函数将是部分的(例如,pair Leaf将是底部的)。由于您实际上并没有使用它们,因此也没有必要,因此我将其删除。
  • 您将键和值包装在一个元组中,没有明显的原因。我将它们分开,然后将它们直接放在类型中。
  • 您的closestLess函数的返回类型为(Integer, v),即使它不能总是返回该类型的内容。我将其更改为Maybe (Integer, v),以便它可以返回Nothing,而不必使用error。 (附带说明:您的错误消息在技术上是错误的。如果您在搜索值小于所有节点的地方调用closestLess,即使树中确实包含元素,它也会失败。)
  • 您的代码在节点的左边和右边是不一致的。在我的代码中,左侧分支始终是数据构造函数中左侧的分支。
  • 您在单独的防护装置中使用了i < fst pairi == fst pair。通过对compare的输出进行大小写匹配,您只需进行一次比较即可,而无需进行两次比较。
  • 您需要一个precise函数,才走上了正确的轨道,但是实际上closestLess中需要包含的许多逻辑都需要包含在其中。

这是一个快速测试案例,使用您链接的站点上的示例:

Prelude> tree = Node 9 () (Node 4 () (Node 3 () Leaf Leaf) (Node 6 () (Node 5 () Leaf Leaf) (Node 7 () Leaf Leaf))) (Node 17 () Leaf (Node 22 () (Node 20 () Leaf Leaf) Leaf))
Prelude> closestLess 4 tree
Just (4,())
Prelude> closestLess 18 tree
Just (17,())
Prelude> closestLess 12 tree
Just (9,())
Prelude> closestLess 2 tree
Nothing

您还可以使其变得更懒惰(一旦找到一个候选者,立即产生外部Just),但会变得更加复杂:

import Data.Functor.Identity

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 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

有关其更多详细信息,请参见my question about this