处理haskell元组的好功能和技巧?

时间:2010-04-06 16:00:12

标签: haskell types

我最近在元组和元组列表方面做了很多工作,我一直在想我是否明智。

对我而言,事情变得尴尬和笨拙,这表明我做错了什么。

例如,我编写了三个便利函数,用于获取3个值的元组中的第一个,第二个和第三个值。

我错过了更好的方法吗?

是否有更多通用函数可以组合和操作元组数据?

以下是我试图做的一些事情应该是可以概括的。

提取值: 我是否需要为大小为2,3,4和5的元组创建一个fst,snd等版本......?

fst3(x,_,_) = x
fst4(x,_,_,_) = x

操纵价值观: 你可以在一对列表中递增每个元组的最后一个值,然后使用相同的函数来递增三元组列表中每个元组的最后一个值吗?

压缩和解压缩值: 有拉链和拉链3。我还需要一个zip4吗?或者有没有办法创建一般的zip功能?

对不起,如果这看起来很主观,老实说我不知道​​这是否可能,或者每次我需要一般解决方案时,我都在浪费时间编写3个额外功能。

感谢您提供任何帮助!

7 个答案:

答案 0 :(得分:8)

提取值

是的,你需要自己写fstN。但为什么不在模式匹配中提取它?

压缩和解压缩值

Data.List已提供最多zip7。对于一般zipN,请使用ZipList。

请参阅How to zip multiple lists in Haskell?

操纵值

不是没有扩展名。由于所有元组都有不同的类型,因此您必须创建一个类型类,例如:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-}

class Firstable a b c | a -> b, a -> c where
    firstOf :: a -> b
    restOf :: a -> c
    concatenate :: b -> c -> a

instance Firstable [a] a [a] where
    firstOf = head
    restOf = tail
    concatenate = (:)

instance Firstable (a,b) a b where
    firstOf = fst
    restOf = snd
    concatenate = (,)

instance Firstable (a,b,c) a (b,c) where
    firstOf (x,_,_) = x
    restOf (_,x,y) = (x,y)
    concatenate x (y,z) = (x,y,z)

instance Firstable (a,b,c,d) a (b,c,d) where
    firstOf (x,_,_,_) = x
    restOf (_,x,y,z) = (x,y,z)
    concatenate x (y,z,w) = (x,y,z,w)

instance Firstable (a,b,c,d,e) a (b,c,d,e) where
    firstOf (x,_,_,_,_) = x
    restOf (_,x,y,z,w) = (x,y,z,w)
    concatenate x (y,z,w,t) = (x,y,z,w,t)

然后你可以使用

incFirst :: (Num b, Firstable a b c) => a -> a
incFirst x = (1 + firstOf x) `concatenate` restOf x

main = do
    print $ map incFirst [(1,2),(3,4),(5,6)]
    print $ map incFirst [(1,3,6,7),(2,5,-2,4)]

lastOf类似。)

但为什么不使用单独的功能?

答案 1 :(得分:7)

当我开始拥有大元组时,我使用Haskell可怜的借口来记录语法,为每个元素命名,例如,

data LatticeOperations a = LO { bot :: a
                              , top :: a
                              , glb :: a
                              , lub :: a
                              , le  :: a
                              }

这是一个五元组,但名称会变成选择单个元素的函数。

对于更改元组,您有记录更新语法。在我刚刚给出的示例中,替换一个元素没有意义,但我可能会,例如,细化部分顺序并替换三个元素

lattice { le = le', glb = glb', lub = lub' }

当然,如果你有一个很大的记录并且只是想增加你可以做类似

的事情
data FatRecord = FR { count :: Int, ... }

fat = fat { count = count fat + 1 }

我不认为记录语法有助于zip和解压缩。

答案 2 :(得分:4)

一旦元组大小超过3,和/或广泛使用相同的元组类型,最好使用记录。

答案 3 :(得分:2)

如果不使用像Template Haskell这样的扩展,就无法对元组大小进行概括。因此,如果我们只考虑普通的haskell:是的,你需要为每个元组大小编写fst等版本,不,你不能写一般的zip方法。

答案 4 :(得分:1)

我最近发现了 { - #LANGUAGE RecordWildCards# - } 允许记录使用字段选择器作为值 - 像这样: data R = R {a :: Int,b :: String,c :: Bool}

testA R {..} = a> 0

或  .. = R {..}其中a = 7; b =“x”; c =真

查看GHC手册!

答案 5 :(得分:1)

迟到总比没有好:尝试Tuple package

答案 6 :(得分:0)

Haskell的内置fstsnd仅支持元组对,因此您定义自己的元组是正确的。如果要增加列表中的最后一个值,请反转列表,从该端开始工作,然后将其反转。如果希望该增量适用于列表和元组列表,只需定义一个新函数,该函数为这些数据类型递增,并在列表的递增函数中调用该函数。 @KennyTM回答了zipN个问题。

使用Haskell中的列表和元组与许多语言略有不同,但经过一段时间后,他们感觉特别自然和强大。

相关问题