【问题标题】:How to create a perfect bijection between two datatypes?如何在两种数据类型之间创建完美的双射?
【发布时间】:2016-07-13 17:28:46
【问题描述】:

有什么策略可以在两种数据类型之间创建双射吗?例如,考虑这些数据类型:

data Colbit 
    = White Colbit Colbit 
    | Black Colbit Colbit 
    | Tip

data Bits
    = B0 Bits
    | B1 Bits
    | BEnd

加上Colbit 的有效元素必须具有奇数个节点(白/黑构造函数)的约束。如何创建地图:

toColbit :: Bits -> Colbit
fromColbit :: Colbit -> Bits

这样对于所有b : BitsfromColbit (toColbit b) == b,以及对于所有c : ColbittoColbit (fromColbit c) == c? (另外,这个属性叫什么?)

【问题讨论】:

  • 我认为没有通用的策略来创建两种类型之间的双射,因为您必须根据两种类型的逻辑来定义双射。你能解释一下你想做什么吗?
  • 我正在尝试将数据类型 Colbit 的元素映射到二进制字符串,因此我可以对它们执行蛮力搜索,以寻找满足某些特定属性的元素。我还希望能够以尽可能少的位数将元素存储在磁盘上。 (如果我只是天真地序列化它,就会有一些不对应Colbit元素的位串,浪费空间。)
  • 为什么不直接映射它们呢?我没明白重点。
  • 我不知道如何将 Colbit 的元素映射到二进制字符串,以使每个二进制字符串都对应于 Colbit 的一个元素,反之亦然。这就是我要问的。
  • 这篇论文是我认为的问题的正确答案:research.microsoft.com/en-us/people/dimitris/…(任何人都应该随意总结关键位以使其成为真正的答案)

标签: algorithm function haskell types functional-programming


【解决方案1】:

第一步是将Colbit的奇数约束转换为类型级别:

{-# LANGUAGE TypeSynonymInstances #-}

data Color = Black | White deriving (Bounded, Enum, Eq, Ord, Read, Show)
data Odd = Evens Color Even Even | Odds Color Odd Odd deriving (Eq, Ord, Read, Show)
data Even = Tip | OddL Color Odd Even | OddR Color Even Odd deriving (Eq, Ord, Read, Show)
type Colbit = Odd

然后,您可以使用我在my previous answer 中描述的技巧来解决您的一个问题,以建立与自然的双射。回顾序言:

type Nat = Integer
class Godel a where
    to :: a -> Nat
    from :: Nat -> a

instance Godel Nat where to = id; from = id

-- you should probably fix this instance to not use
-- Double if you plan to use it for anything serious
instance (Godel a, Godel b) => Godel (a, b) where
    to (m_, n_) = (m + n) * (m + n + 1) `quot` 2 + m where
        m = to m_
        n = to n_
    from p = (from m, from n) where
        isqrt    = floor . sqrt . fromIntegral
        base     = (isqrt (1 + 8 * p) - 1) `quot` 2
        triangle = base * (base + 1) `quot` 2
        m = p - triangle
        n = base - m

instance (Godel a, Godel b) => Godel (Either a b) where
    to (Left  l) = 0 + 2 * to l
    to (Right r) = 1 + 2 * to r
    from n = case n `quotRem` 2 of
        (l, 0) -> Left  (from l)
        (r, 1) -> Right (from r)

有了这些,我们类型的实例就很容易了。

monomorph :: Either a a -> Either a a
monomorph = id

toColored :: Godel v => (Color, v) -> Nat
toColored (Black, v) = to (monomorph (Left  v))
toColored (White, v) = to (monomorph (Right v))

fromColored :: Godel v => Nat -> (Color, v)
fromColored n = case from n of
    Left  v -> (Black, v)
    Right v -> (White, v)

instance Godel Odd where
    to (Evens c l r) = 0 + 2 * toColored (c, (l, r))
    to (Odds  c l r) = 1 + 2 * toColored (c, (l, r))
    from n = case n `quotRem` 2 of
        (clr, 0) -> Evens c l r where (c, (l, r)) = fromColored clr
        (clr, 1) -> Odds  c l r where (c, (l, r)) = fromColored clr

instance Godel Even where
    to Tip = 0
    to (OddL c l r) = 1 + 2 * toColored (c, (l, r))
    to (OddR c l r) = 2 + 2 * toColored (c, (l, r))
    from 0 = Tip
    from n = case (n-1) `quotRem` 2 of
        (clr, 0) -> OddL c l r where (c, (l, r)) = fromColored clr
        (clr, 1) -> OddR c l r where (c, (l, r)) = fromColored clr

差不多就是这样。现在你已经有了自然的双射,你可以在自然和比特流之间选择你最喜欢的双射来进行后期合成。

【讨论】:

  • @dfeuer 您提议的实例不在上(例如,5 不是任何对的图像)。
  • 我刚刚意识到使用 little-endian naturals 进行编码应该非常容易,旨在确保没有多余的零。 newtype Natural = Nat 'Truedata Nat mayzero where Zero :: Nat 'False -> Nat may; One :: Nat may -> Nat be; End :: Nat 'True。可以通过交错形成对。
  • @dfeuer 交错比你想象的要复杂。您需要知道较短的序列何时结束。另请参阅 the link from my previous answer,了解配对函数的精妙艺术,以及特别干净且可流式传输的配对函数的定义。
  • 那是一篇有趣的论文。谢谢。我对交织方法的目标不是 效率,因为他们的目标是,而是 正确性和简单性,使用整数平方根的方法不可能同时达到。跨度>
  • @dfeuer 您的目标令人钦佩,但据我所知,您的解决方案并不正确。他们的目标可能与您的不同,但他们的解决方案符合您的标准,即明确正确且非常简单。 (我之所以避免它,只是因为它在Integers 上的实现不如在位列表上方便——这意味着要么实现不方便,要么引入胶水代码。其中任何一个都会分散主要思想的注意力。)
猜你喜欢
  • 2012-12-25
  • 1970-01-01
  • 2017-12-13
  • 1970-01-01
  • 2013-01-07
  • 1970-01-01
  • 1970-01-01
  • 2013-12-24
  • 2023-01-21
相关资源
最近更新 更多