【问题标题】:Power set using BST in haskell在 haskell 中使用 BST 的电源集
【发布时间】:2022-01-06 19:19:49
【问题描述】:

我在 Haskell 中使用二叉搜索树实现了一个 Set 数据类型。我没有使用任何内置函数,也没有导入任何模块。

我设置的数据类型如下:

data Set a = Empty | Node a (Set a) (Set a) deriving(Show) 

我已经编写了一个 toList 函数,以及一个使用 insert 函数的 fromList 函数。 我还使用 bst 在集合上编写了一个 setmap 和一个 setfoldr 函数。

我现在只解决一个问题:

-- powerset of a set
-- powerset {1,2} => { {}, {1}, {2}, {1,2} }
powerSet :: Set a -> Set (Set a) 

我不知道如何使用这种类型签名来实现 powerSet。对于我需要为此编写什么样的辅助函数,我没有任何线索。我对如何使用列表执行此操作有一些线索,但不使用二叉搜索树。如果可以,请分享一些有关如何执行此操作的提示。 提前致谢!

【问题讨论】:

  • 所以你知道如何写toList,如何找到列表的幂集,以及如何写fromList,但不知道如何找到Set的幂集?
  • "如果你能把这个功能的代码分享给我。"
  • @MichaelLitchard- 我只需要一些提示,已编辑该行,谢谢
  • @JosephSible-ReinstateMonica,是的,我编写了所有其他函数,但我不确定如何使用这种类型签名编写这个函数
  • @JosephSible-ReinstateMonica NB 那是fromList,而不是fromAscendingList。所以他们需要insert,这需要Sets 上的Ord。也许这就是他们遇到的问题。他们真的应该实现fromAscendingList 以在此处使用(在上一个问题中,我没有实现该功能并且他们没有跟进)。

标签: haskell set powerset


【解决方案1】:

幂集是递归定义的。

空集的幂集是包含空集的集合。

P({}) = {{}}

非空集S 的幂集P 是通过首先选择任意元素x 并将其从S 中删除得到S' 来找到的。您递归地竞争S' 的幂集以获得P'。然后将P 构造为

P = P' ∪ { s ∪ {x} | s ∈ P' }

所以,只要定义了union :: Set a -> Set a -> Set aremove :: Set a -> a -> Set a,就可以直接将上述定义翻译成powerset :: Set a -> Set (Set a)

(我从上面的所有类型签名中省略了假设的Ord a 约束。)

例如,

P({}) == {{}}
P({1}) == {{}} ∪ {{1}}
       == {{}, {1}}
P({1,2}) == {{}, {1}} ∪ {{2}, {1,2}}
         == {{}, {1}, {2}, {1,2}}

等等

【讨论】:

  • 你真的不需要(我的意思是,不应该)比较子集来构建powerset。这只是一个结构性建筑。另外,pickOne :: Set a -> (a, Set a) 似乎是一个更好的选择。 ;)(它调用,仅用于非空集,通过构造)。
  • pickOne 是个好主意。我看不到我在哪里比较任何子集。 (最后一个块应该是 ASCII 数学,而不是 Haskell 伪代码。)
  • 你确实写过你已经“省略了假设的Ord a约束”......并且union需要比较元素,这些元素是Sets。 (或者可能不是......?)
  • 是的,它们在P' ∪ ... 部分(不在s ∪ {x} 部分)。
  • 您是否提议将powerset 的类型改为Set a -> [Set a]?任何在 BST 上运行的函数都需要 Ord a 约束。
【解决方案2】:

你提到你已经实现了toList。你可以用它去这里的列表路线。

就像在您的previous question 中一样,这需要实现和使用fromAscendingList,以纯粹结构的方式从列表构造一棵树,没有任何比较,假设列表已经按递增顺序排列顺序。

这种结构构造不涉及任何元素知识; powerset 函数也应该如此:

powerSet :: Set a -> Set (Set a)
powerSet = toList >>> powerList >>> map fromAscendingList
                  >>> fromAscendingList

-- (foo >>> bar >>> baz) x = baz (bar (foo x))

当然,我们需要以保持顺序的方式实现powerList,这样才能正常工作:

powerList :: [a] -> [[a]]
powerList  =  concat . powers
  where
  powers :: [a] -> [[[a]]]
  powers []     =  [[[]]]
  powers (x:xs) =  let { p = powers xs } in 
          [[]] : zipWith (++)
                         (map (x:) <$> p)
                         (drop 1 p ++ [[]])

-- > powerList [1,2,3]
-- [[],  [1],[2],[3],  [1,2],[1,3],[2,3],  [1,2,3]]

(一个更简单的替代方法以不同的顺序生成子列表:

powerList' :: [a] -> [[a]]
powerList' (x:xs) = [ s | s <- powerList' xs, s <- [s, x:s]]
powerList' []     = [[]]

-- > powerList' [1,2,3]
-- [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

如果我们直接在另一个答案中遵循 set-notation 伪代码,则生成的代码将生成子列表更加乱序 - [3] 将在 [2] 之前出现,并且在 @987654331 之前出现@)。

你仍然需要实现fromsAcendingList。最简单的方法是创建高度不平衡的、类似列表的树。你可以从那开始。然后也许设计一种方法来创建近似平衡的树,这是更可取的。

作为替代方案,将以上所有内容视为可执行规范并重新实现它以直接处理您的 Set 类型值。 mapTree 是微不足道的(并且已经包含在您的 previous question 中);同时访问边缘节点及其后继节点也是可行的。

【讨论】:

  • 嘿。感谢您的回答。 fromAscendingList 是这样的吗: inser :: a-> Set a -> Set a inser a Empty = singleton a inser a (Node val l r) = Node val (inser a l) r frmAsclst :: [a] -> Set a frmAsclst = foldr inser 空 ///
  • 是的,这行得通(您可以在 GHCi REPL 上尝试一下,然后自己看看)。当然,它是更糟糕的一种,一种高度不平衡的,即所谓的“退化”形状。但它有效;您可以检查toList (frmAsclst xs) 是否重新创建了原始列表xs。接下来,尝试使用手动递归来创建更平衡的树。如果你有前半部分、中间元素和后半部分,那么......(尝试完成这个)。或者您可以让您的 inser 根据需要进行树旋转。
  • 我正在尝试执行 powerset 函数,但它一直抛出错误,即使逻辑是正确的。无法匹配预期类型和实际类型错误。例如:预期:设置 a0 -> 设置 a -> 设置(设置 a) 实际:设置 a0 -> [a0] ,用于 toList 函数。
  • 我已经用frmAsclst [1..3] &amp; powerSet &amp; toList &amp; map toList 进行了测试,它可以工作。 (与x &amp; f = f x
  • 您的toList 代码是什么?编辑您的问题以包含它。你说你已经实现了?问题到底出在哪里?无论如何,也许可以在一个地方包含所有代码的新帖子,即发布一个新问题。这个答案已经回答了所问的问题。新问题——新帖子,就是这样。 :) 你应该通过接受最有帮助的答案(如果有的话)来结束这个(和上一个)问题并继续前进。
猜你喜欢
  • 2011-12-07
  • 1970-01-01
  • 1970-01-01
  • 2020-03-14
  • 2017-04-26
  • 1970-01-01
  • 2018-06-30
  • 2020-06-16
  • 2017-07-24
相关资源
最近更新 更多