【问题标题】:Haskell dynamically transpose multiple lists to single list recursivelyHaskell 递归地将多个列表动态转置为单个列表
【发布时间】:2020-03-21 09:22:47
【问题描述】:

不确定“动态”是否适合这个问题。

我一直在努力寻找压缩多个列表的解决方案 进入一个列表,但我开始认为我的方法是不可能的。 它适用于两个列表,但不适用于三个(可能不会超过三个)。 该函数应该作为transpose 工作,但会产生一个整数列表和 不是整数列表的列表。但如果它产生一个列表列表,它也会 工作。理论上,该函数应该适用于任意数量的列表。

例如 [1,2,3] myTranspose [4,5,6] myTranspose [7,8,9] 应该会导致 [1, 4, 7, 2, 5, 8, 3, 6, 9]

这可能吗?

到目前为止我的尝试:

myTranspose :: [Int] -> [Int] -> [Int]
myTranspose [] []         = []
myTranspose [x] []        = [x]
myTranspose [] [y]        = [y] 
myTranspose [x] [y]       = [x,y]
myTranspose (x:xs) [y]    = (x:y:xs)
myTranspose [x] (y:ys)    = (x:y:ys)
myTranspose (x:xs) (y:ys) = [x,y] ++ zip' xs ys

编辑我的实际问题:

我应该从一开始就问这个,但我认为它会 将问题转换为整数列表更简单。对此感到抱歉。

我有一个数据类型 Function 和一个链接 Functions 的函数 一起:

data Function where
    (|||) :: Function -> Function -> Function
    A ::  Char -> Function
    B ::  Int  -> Function

chain :: Function -> Function -> Function
chain f1 f2 = f1 ||| f2

而且我还有一个功能应该像我上面描述的那样工作:

(<|||>) :: Funcion -> Function -> Function
 ...something like this..
(<|||>) (p1 ||| p2) (q1 ||| q2)     =  (p1 <|||> q1) ||| (p2 <|||> q2)
(<|||>) (p1 ||| p2)       q          =  p1 <|||> p2 ||| q 
(<|||>)       p         (q1 ||| q2)  =  p  ||| q1 <|||> q2
(<|||>)       p           q          =  p  ||| q

我设法用普通的列表和普通的 像moonGoose建议的转置功能。但问题是 如果我这样做,编译器会抱怨没有足够的内存 那样。 函数调用看起来像这样,除了列表是 非常大:

transpose ((A 'a' ||| B 5 ||| A 'n')   <|||>  
             (A 'o' ||| B 3 ||| A 'p') <|||>
            (A 'i' ||| B 0 ||| A 'l'))

但是当我使用“我的”列表功能和不完美的自定义运行程序时 transpose-function 编译器不会抱怨。我的想法是,这与懒惰有关。这可能是问题吗? 感谢您的帮助。

【问题讨论】:

  • 我认为您必须为 2 对 3 输入列表编写不同的版本。这不是压缩,而是当您一次从每个列表中获取一个元素而不是从您正在获取的输入中交替时,这个名称甚至暗示了一个有用的实现技巧。 alt [] ys = ys; alt (x:xs) ys = x : alt ys xs 并且很容易看到如何将其扩展到 3+ 输入列表。
  • concat . transpose 有什么问题?提供您想要实现的目标的完整背景会很有帮助。
  • 您的规范在数学上看起来是不可能的。它似乎要求[1,2] t [3,4] 为[1,3,2,4],以及[1,3,2,4] t [5,6] 为[1,5,3,6,2,4],但您还希望([1,2] t [3,4]) t [5,6] 为[1,3,5,2,4,6],与之前的结果相冲突。
  • 我迷路了。您说“当我使用列表函数和不完美的自定义转置函数运行程序时,编译器不会抱怨”。这是否意味着你已经解决了你的问题?如果不是,为什么不呢?关于不同的代码库,您说“问题是编译器抱怨没有足够的内存”。我怎样才能自己重现这个观察结果?我投票结束,因为这对于您所面临的确切问题以及导致它的确切代码是多么模糊。
  • @datamoose 告诉我您遇到内存错误是无用,而无需说明重现内存错误的确切步骤。它可能是您的程序中的某些内容,或者您​​运行它的方式,或者如果我无法重现它的话。

标签: algorithm haskell


【解决方案1】:

也许你可以动态地这样做

zwh :: [[a]] -> [a]
zwh xss = if any ((== 0) . length) xss then []
                                       else hs ++ zwh ts
          where
          (hs,ts) = foldr (\(hs',ts') (rs,qs) -> (hs':rs,ts':qs)) ([],[]) hts
          hts     = (,) <$> head <*> tail <$> xss

λ> zwh [[1,2,3],[4,5,6],[7,8,9]]
[1,4,7,2,5,8,3,6,9]
λ> zwh [[1,2],[4,5,6],[7,8,9]]
[1,4,7,2,5,8]

或者根据a的类型,如果可以包含EQ a =&gt;约束,它会进一步简化为

zwh :: Eq a => [[a]] -> [a]
zwh xss = if any (== []) xss then []
                             else hs ++ zwh ts
          where
          (hs,ts) = foldr (\(hs',ts') (rs,qs) -> (hs':rs,ts':qs)) ([],[]) hts
          hts     = (,) <$> head <*> tail <$> xss

【讨论】:

  • 谢谢,但它必须具有类型 Function -> Function -> Function
【解决方案2】:

编辑后,我想你想要这样的东西?正如 chi 在评论中已经提到的那样,这令人困惑,因为 (|||) 似乎是关联的,这可能是您遇到的问题的核心。如果它是关联的,那么底层结构是(非空)类似列表的,所以我们知道如何对它们进行转置并将它们轻松粘合在一起等。但是如果它不是关联的,那么表示是树状的喜欢,而且我认为您不能规范地映射/转置树(例如,A 0 &lt;&gt; A 1 &lt;&gt; A 2 = (A 0 ||| A 1) ||| A 2 或 A 0 ||| (A 1 ||| A 2)?这很重要,前提是 (|||) 不是关联的)。

import Control.Lens.Iso (iso, mapping, under)
import qualified Data.List.NonEmpty as NE
import Data.List.NonEmpty (NonEmpty)
import Data.Semigroup (sconcat)

instance Semigroup Function where
  (<>) = (|||)

transposeFuncs :: NonEmpty Function -> Function
transposeFuncs = sconcat . under (mapping $ iso reify reflect) NE.transpose
  where
    reflect :: Function -> NonEmpty (Either Char Int)
    reflect (a ||| b) = reflect a <> reflect b
    reflect (A a)     = pure $ Left  a
    reflect     (B b) = pure $ Right b
    reify :: NonEmpty (Either Char Int) -> Function
    reify = sconcat . NE.map (either A B)
transposeFuncs $ NE.fromList 
  [ A 'a' ||| B  5  ||| A 'n'
  , A 'o' ||| B  3  ||| A 'p'
  , A 'i' ||| B  0  ||| A 'l'
  ]
  ==  A 'a' ||| A 'o' ||| A 'i'
  ||| B  5  ||| B  3  ||| B  0
  ||| A 'n' ||| A 'p' ||| A 'l' 

编辑: 如果您希望在数据类型中表达关联性,那么您将使用类似以下的内容。看看当类型与意图匹配时有多好;每个(语义)值只有一种表示形式。如果你允许一个身份,那么它会更干净。

import Control.Lens (makeWrapped, mapping, under, _Unwrapped')
import Data.List.NonEmpty (NonEmpty, transpose)
import Data.Semigroup (sconcat)

data FuncAtom
  = A Char
  | B Int

newtype Function
  = FCompose (NonEmpty FuncAtom)
  deriving Semigroup via NonEmpty FuncAtom
makeWrapped ''Function

transposeFuncs :: NonEmpty Function -> Function
transposeFuncs = sconcat . under (mapping _Unwrapped') transpose

【讨论】:

  • 这可能有效。详细了解后会尽快尝试,谢谢。
猜你喜欢
  • 1970-01-01
  • 2012-09-29
  • 1970-01-01
  • 2019-06-05
  • 2019-07-21
  • 2020-03-25
  • 1970-01-01
  • 2015-12-03
  • 1970-01-01
相关资源
最近更新 更多