在Haskell中,我如何最好地将列表转换为元组:
[1,2,3,4,5,6] -> (1,2,3,4,5,6)
一般情况下,是无法做到的。每个元组大小都是一种不同的类型,而长度任意的列表则是单一类型。因此,很难编写一个函数,它可以接受一个列表并返回相同长度的元组——这样的函数将没有明确定义的返回类型。
比如,你可以有这样的函数:
tuplify2 :: [a] -> (a,a)
tuplify2 [x,y] = (x,y)
tuplify3 :: [a] -> (a,a,a)
tuplify3 [x,y,z] = (x,y,z)
...但是没有一种同时完成这两个任务的方法。
你可以使用各种元编程技术编写一个通用版本,但你很少会这样做。
请注意,同样的问题也适用于其他情况,例如为不同的元组编写类实例 - 查看标准库中Data.Tuple的源代码!
如果你想提取可变数量的元素,并且由于类型检查,(a,b)和(a,b,c)具有不同的类型,那么Template Haskell是你能够接近的最好方法。
{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
tuple :: Int -> ExpQ
tuple n = do
ns <- replicateM n (newName "x")
lamE [foldr (\x y -> conP '(:) [varP x,y]) wildP ns] (tupE $ map varE ns)
那么:
$(tuple 6) [1,2,3,4,5,6] == (1,2,3,4,5,6)
$(tuple 3) "abc" == ('a','b','c')
总的来说,如果你需要这个答案,那么你可能在某个地方问错了问题。
如果你只是想要平面的随机访问,也许更好的选择是使用一个数组。
Control.Lens
与 map (^..each)
更接近,它也适用于任意元组。——并不是所有“针对任意元组”的东西都与元组的整个目的有关。 (这就是列表的作用。当需要防止这种用法时,才会使用元组。) - anoneach
使用惰性模式的方式:ghci> undefined & partsOf each .~ [1,2,3,4] :: (Int,Int,Int,Int)
会产生(1,2,3,4)。因此,您可以双向操作,直到大小约为9。当然,您需要知道结果元组类型。 - Edward Kmett我感觉好像要建议你把枪指向你的脚,然后相信你不会开枪。
> list2Tuple lst = read $ "(" ++ (init.tail.show) lst ++ ")"
> list2Tuple [1,2,3] :: (Int, Int, Int)
(1,2,3)
> list2Tuple [1,2,3,4] :: (Int, Int, Int, Int)
(1,2,3,4)
这将适用于 Show 和 Read 定义的任何元组长度。
toTuple :: [a] -> (a,a,a,a,a,a)
toTuple [a,b,c,d,e,f] = (a,b,c,d,e,f)
注意类型的不同:列表中的单个变量扩展为元组的六个变量。因此,您需要为每个元组大小编写一个函数。
我发现很难清楚地解释模板Haskell操作,但这里有一个演示:
> :m +Language.Haskell.TH
> :set -XTemplateHaskell
> runQ [| [1,2,3,4,5,6] |] >>= putStrLn . pprint
[1, 2, 3, 4, 5, 6]
> runQ [| [1,2,3,4,5,6] |] >>= \ (ListE exps) -> putStrLn (pprint (TupE exps))
(1, 2, 3, 4, 5, 6)
在处理命令行参数时,您可以使用getArgs
函数,该函数将为您提供一个字符串列表:
getArgs :: IO [String]
链接: https://hackage.haskell.org/package/base-4.16.0.0/docs/System-Environment.html#v:getArgs
当我处理命令行参数时,我更喜欢使用元组而不是列表,因此我将列表转换为元组。请参见下面的代码:
import System.Environment
main = do
args <- getArgs
let (a:b:c) = args
print a
调用程序(在PowerShell中):
PS C:\Haskell> runghc convertListToTuple goodday to you
"goodday"
Data.Tuple
的真正源代码呢?我的意思是... 导出列表中的第二个元素是:, snd -- :: (a,b) -> a
。这个类型上的明显错误真的存在吗? - Bakuriusnd
的类型签名是正确的。那也是一个相当旧的版本的Data.Tuple
,更新的版本没有这个错误。 - C. A. McCann