自定义树数据类型的 Haskell 模式匹配以减少重复代码

问题描述

所以,我是 Haskell 的新手,我有这个函数可以在当前状态下工作,但是代码中有很多重复。这是用于学校分配的,因此我无法更改数据类型声明或 makeWeightedTree 输入/输出

我有一个自定义的加权树结构 (Wtree),我需要通过将叶子 (L) 链接到分支 (B) ([ Wtree] -> [Wtree])。为此,我目前正在对 Wtree 构造函数的每个组合进行模式匹配,这会产生大量重复,我还不知道如何在 Haskell 中解决这些问题。

这是我的自定义数据类型:

data Wtree = L Integer Char | B Integer Wtree Wtree deriving (Show)

这是我正在使用的函数

makeWeightedTree :: [Wtree] -> [Wtree]
makeWeightedTree []  = []   -- No Leafs in input,Done                                      
makeWeightedTree [x] = [x]  -- Done

-- Wtree Leaf and Leaf
makeWeightedTree (t1@(L w1 _) : t2@(L w2 _) : xs)
    | w1 > w2   = makeWeightedTree $ sortBy sortNodes (t1:t2:xs)
    | otherwise = makeWeightedTree $ B (w1+w2) t1 t2 : xs 

-- Wtree Leaf and Branch
makeWeightedTree (t1@(L w1 _) : t2@(B w2 _ _) : xs)
    | w1 > w2   = makeWeightedTree $ sortBy sortNodes (t1:t2:xs)
    | otherwise = makeWeightedTree $ B (w1+w2) t1 t2 : xs 

-- Wtree Branch and Leaf
makeWeightedTree (t1@(B w1 _ _) : t2@(L w2 _) : xs)
    |  w1 > w2  = makeWeightedTree $ sortBy sortNodes (t1:t2:xs)
    | otherwise = makeWeightedTree $ B (w1+w2) t1 t2 : xs 

-- Wtree Branch and Branch 
makeWeightedTree (t1@(B w1 _ _) : t2@(B w2 _ _) : xs)
    |  w1 > w2  = makeWeightedTree $ sortBy sortNodes (t1:t2:xs)
    | otherwise = makeWeightedTree $ B (w1+w2) t1 t2 : xs 

有什么聪明的方法可以绕过守卫中的重复台词吗?我认为 case-expression 可以工作,但我不熟悉它,所以我没有做任何事情。感谢您提供任何帮助或提示

编辑:我的解决方

根据反馈,我认为这对我来说是最合适的解决方案,因为我对 Haskell 或一般的函数式编程还不太熟悉。

makeWeightedTree :: [Wtree] -> [Wtree]
makeWeightedTree []  = []      -- No Wtree(s),Done      
makeWeightedTree [t] = [t]     -- Done
makeWeightedTree t@(t1:t2:xs) 
    | w1 > w2   = makeWeightedTree $ sortBy sortNodes t
    | otherwise = makeWeightedTree $ B (w1+w2) t1 t2 : xs 
    where 
        w1 = getWeight t1
        w2 = getWeight t2

getWeight :: Wtree -> Integer
getWeight (L w _)    = w
getWeight (B w _ _ ) = w 

解决方法

看起来您关心的主要事情是获得 WTree 的权重,因此我建议您将其抽象出来。您可以编写如下函数:

weightOfTree :: WTree -> Integer
weightOfTree = ... -- use pattern matching

然后,您似乎可以将这八个 makeWeightedTree 情况减少到两个:

makeWeightedTree (t1 : t2 : xs) = 
  if w1 > w2 then ... else ...
  where
    w1 = weightOfTree t1
    w2 = weightOfTree t2
,

除了@DDub 制作辅助函数来提取任意树的权重的想法之外,还为您的类型定义一个 Semigroup 实例:

data Wtree = L Integer Char | B Integer Wtree Wtree deriving (Show)

weight :: Wtree -> Integer
weight (L w _) = w
weight (B w _ _) = w

instance Semigroup Wtree where
    t1 <> t2 = B (weight t1 + weight t2) t1 t2

那么 makeWeightedTree 就是简单的

makeWeightedTree :: [Wtree] -> [Wtree]
makeWeightedTree [] = []
makeWeightedTree xs = [sconcat (sortBy sortNodes xs)]

(这与您的函数略有不同,但不清楚为什么对整个列表进行一次排序而不是只关心前两个元素在每一步都进行本地排序会很重要。)


您似乎也在使用 [] 的地方使用了 Maybe,因为您总是将 Wtree 的非空列表减少到单个 Wtree .

makeWeightedTree :: [Wtree] -> Maybe Wtree
makeWeightedTree [] = Nothing
makeWeightedTree xs = Just $ sconcat (sortBy sortNodes xs)