![](/img/trans.png)
[英]Best way to turn a list of lists into a more nested list of lists of lists with equal length?
[英]What is the easiest way to turn a list with known length into nested pairs in Haskell?
如何将具有已知长度的列表转换为嵌套对? 换句话说,填充下面的类型孔最方便的方法是什么?
_ [1,2] :: (Int,Int)
_ [1,2,3] :: ((Int,Int),Int)
_ [1,2,3,4] :: (((Int,Int),Int),Int)
_ [1,2,3,4,5] :: ((((Int,Int),Int),Int),Int)
编辑:请注意,类型孔不必是相同的功能,我正在寻找一个方便的模式(如果存在方便的模式)来填补空洞。
也许是这样的:
step f xs = (f (init xs), last xs)
len1 = head
len2 = step len1
len3 = step len2
len4 = step len3
在ghci:
*Main> len4 [1..4]
(((1,2),3),4)
当然也可以通过模式匹配直接实现这些功能之一:
len4' [a,b,c,d] = (((a,b),c),d)
这也不会像有元素一样遍历列表,这很好。
与依赖类型的版本一起使用。 首先,让我们完成样板:
{-# LANGUAGE
TemplateHaskell, DataKinds, ScopedTypeVariables,
FlexibleInstances, PolyKinds, TypeOperators,
TypeFamilies, GADTs, UndecidableInstances #-}
import Data.Singletons.TH
import qualified GHC.TypeLits as Lit
$(singletons [d| data Nat = Z | S Nat deriving (Eq, Show) |])
这里使用TH纯粹是为了减少样板,我们不会在实际代码中使用TH。 实际上,上面的内容可以(并且应该)在某个地方的一个包中被考虑(在写这个答案时,没有这样一个包含最新singletons
依赖的包)。
tuplify
成为一个函数,其返回类型取决于Nat
参数。
type family NTup n a where
NTup (S (S Z)) a = (a, a)
NTup (S (S (S n))) a = (NTup (S (S n)) a, a)
tuplify :: Sing n -> [a] -> NTup n a
tuplify n as = go n (reverse as) where
go :: Sing n -> [a] -> NTup n a
go (SS (SS SZ)) [a, b] = (b, a)
go (SS (SS (SS n))) (a:as) = (go (SS (SS n)) as, a)
go _ _ = error "tuplify: length mismatch"
尝试一下:
tuplify (SS (SS (SS SZ))) [1, 2, 3] -- ((1, 2), 3)
现在写出自然是非常艰巨的,所以让我们介绍一些语法糖:
type family N n where
N 0 = Z
N n = S (N (n Lit.- 1))
type SN n = Sing (N n)
现在:
tuplify (sing:: SN 10) [1..10] -- (((((((((1,2),3),4),5),6),7),8),9),10)
作为旁注,如果我们将空列表转换为()
(从而也允许单元素嵌套元组),我们的定义变得更加自然:
type family NTup n a where
NTup Z a = ()
NTup (S n) a = (NTup n a, a)
tuplify :: Sing n -> [a] -> NTup n a
tuplify n = go n . reverse where
go :: Sing n -> [a] -> NTup n a
go SZ [] = ()
go (SS n) (a:as) = (go n as, a)
go _ _ = error "tuplify: length mismatch"
tuplify (sing:: SN 5) [1..5] -- ((((((),1),2),3),4),5)
这对于依旧类型的Agda来说是一个很好的练习。 在Haskell中,你可以获得一些接近的东西(也受到Daniel Wagner解决方案的启发)
class C a b where
listToTuple :: [a] -> b
instance C a a where
listToTuple [x] = x
instance C a b => C a (b,a) where
listToTuple xs = (listToTuple (init xs), last xs)
一些测试:
> listToTuple [1..3::Int] :: ((Int,Int),Int)
((1,2),3)
> listToTuple [0..3::Int] :: (((Int,Int),Int),Int)
(((0,1),2),3)
请注意,返回类型注释是必需的,否则Haskell无法推断返回元组必须具有的元素数量。 如果元组和列表长度不匹配,则会发生运行时错误。 这几乎是不可避免的,因为列表不会在其类型中携带它们的长度,因此编译器不能更早地检查它(与使用向量GADT不同)。
为了拥有这样的泛型和类型安全函数,您需要依赖类型,以便结果中嵌套元组的数量可能取决于输入列表的长度。
然而,有可能接近多态递归 。
让我们定义一个数据类型如下:
data TupleList' r a = Value r | Tuple (TupleList' (r, a) a)
deriving (Show, Read, Eq, Ord)
type TupleList = TupleList' ()
因此,类型TupleList a
的值与()
, ((), a)
, (((), a), a)
TupleList a
构,这取决于有多少Tuple
构造函数包装最终的Value
。
现在我们可以将列表转换为如下的元组:
fromList :: [a] -> TupleList a
fromList = loop ()
where
loop :: r -> [a] -> TupleList' r a
loop r [] = Value r
loop r (x:xs) = Tuple (loop (r, x) xs)
请注意, loop
使用多态递归(作为对TupleList'
进行操作的任何函数TupleList'
- 其递归调用具有签名(r, a) -> [a] -> TupleList' (r, a) a
。
示例: mapM_ (print . fromList) (inits [1..4])
产生
Value ()
Tuple (Value ((),1))
Tuple (Tuple (Value (((),1),2)))
Tuple (Tuple (Tuple (Value ((((),1),2),3))))
Tuple (Tuple (Tuple (Tuple (Value (((((),1),2),3),4)))))
最简单的方法是
z (x:xs) = x
s r (x:xs) = (x, r xs)
toTuples n xs = n xs
但toTuples
以相反的顺序返回对:
toTuples (s (s (s z))) [1..] == (1,(2,(3,4)))
我们可以使用CPS来解决这个问题:
z f xs = f ()
s r f (x:xs) = r (\p -> (f p, x)) xs
toTuples n (x:xs) = n (const x) xs
然后
toTuples (s (s (s z))) [1..] == (((1,2),3),4)
我们可以定义一些句法糖(我主要是从AndrásKovács的回答中窃取):
{-# LANGUAGE TemplateHaskell, UndecidableInstances, DataKinds, GADTs, TypeFamilies, TypeOperators #-}
import Data.Singletons.TH
import GHC.TypeLits
$(singletons [d| data Nat = Z | S Nat deriving (Eq, Show) |])
z f xs = f ()
s r f (x:xs) = r (\p -> (f p, x)) xs
toTuples n (x:xs) = n (const x) xs
type family Result n r a where
Result Z r a = r
Result (S n) r a = Result n (r, a) a
run :: Sing n -> (() -> r) -> [a] -> Result n r a
run SZ = z
run (SS sn) = s (run sn)
toTuplesN :: Sing n -> [a] -> Result n a a
toTuplesN sn (x:xs) = run sn (const x) xs
type family N n where
N 0 = Z
N n = S (N (n - 1))
type SN n = Sing (N (n - 1))
main = print $ toTuplesN (sing :: SN 6) [1..] -- (((((1,2),3),4),5),6)
请注意,代码也适用于无限列表,因为没有反转。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.