“替换”一个3元组

时间:2011-12-13 04:20:28

标签: haskell

我有以下列表(这是一个长度为2的列表,但在我的作业中我有一个长度+ n列表)

xxs = [(11,22,[(33,33,33),(44,44,44)]),(55,66,[(77,77,77),(88,88,88)])]

我正试图通过列表索引(n)和子列表索引(p“替换”一个3元组(p1或p2或p3或p4来自下图) )。

Visual of list breakdown

该功能最后应该是:

fooo newtuple n p = (…)

例如:(将p3替换为(98,98,98):

fooo (98,98,98) 2 1 
[(11, 22, [(33,33,33) , (44,44,44)]) , (55, 66, [(98,98,98),(88,88,88)])]  

我按照以下步骤计划了代码:

  1. 访问我想要更改的pn。我设法通过以下方式实现它:

    fob n p = ((aux2 xxs)!!n)!!p
       where aux2 [] = []
             aux2 ((_,_,c):xs) = c:aux2 xs
    
  2. “替换”3元组。我真的需要一些帮助。我被卡住了。我做过的最好的代码(在我的脑海里有点意义):(记住:请不要对我的代码太糟糕,我只研究Haskell只有5周)

    foo n p newtuple = fooAux newtuple fob 
         where fooAux _ [] = [] 
               fooAux m ((_):ds) = m:ds
               fob n p = ((aux2 xxs)!!n)!!p
                  where aux2 [] = []
                        aux2 ((_,_,c):xs) = c:aux2 xs
    
  3. 最后,我将使用splitAt将所有内容重新组合在一起。

  4. 我对这个问题的处理方法是否正确?我真的很感激第2步的一些帮助。

3 个答案:

答案 0 :(得分:7)

我对Haskell也有点新意,但是让我们看看我们是不是想出一个像样的方法。

因此,从根本上说,我们要做的是修改列表中的内容。使用函数式编程我想保持它有点一般,所以让我们创建一个函数update

update :: Int -> (a -> a) -> [a] -> [a]
update n f xs = pre ++ (f val) : post
  where (pre, val:post) = splitAt n xs

现在将采用索引,函数和列表,并将列表中的nth元素替换为应用于其的函数的结果。

然而,在我们更大的问题中,我们需要在嵌套的上下文中进行更新。幸运的是,我们的update函数将函数作为参数,因此我们也可以在该函数内调用update

type Triple a = (a,a,a)
type Item = (Int, Int, [Triple Int])

fooo :: Triple Int -> Int -> Int -> [Item] -> [Item]
fooo new n p = update (n-1) upFn
   where upFn (x,y,ps) = (x,y, update (p-1) objFn ps)
         objFn _ = new

所有fooo所要做的就是调用更新两次(一次在另一个调用中)并执行一些“内务”工作(将结果正确地放入元组中)。 (n-1)(p-1)是因为您似乎从1开始编制索引,而Haskell从0开始。

让我们看看它是否适用于我们的测试用例:

*Main> fooo (98,98,98) 2 1 [(11,22,[(33,33,33),(44,44,44)]),(55,66,[(77,77,77),(88,88,88)])]
[(11,22,[(33,33,33),(44,44,44)]),(55,66,[(98,98,98),(88,88,88)])]

答案 1 :(得分:4)

所以你尝试使用一些现成的函数(!!)。它可以为您访问列表中的项目,但忘记了它的位置,因此无法更新。你有一个解决方案,使用另一个现成的函数split,将列表分成两部分,(++)将它们粘合成一部分。

但为了真正感受到它,我怀疑你的任务首先是针对(很容易忘记一个功能名称,而且同样容易为自己写一个新的),您可以尝试自己编写第一个(!!)。然后你会发现修改它真的很容易,所以它也可以更新列表。

要编写函数,最好将其视为等价方程:

myAt 1 (x:xs) = x
myAt n (x:xs) | n > 1 = ...

n为零时,我们只是拿掉头元素。如果不是,我们该怎么办?我们试图接近零。你可以填空。

所以在这里我们返回了找到的元素。如果我们想要更换它怎么办?用什么代替? - 这会调用另一个参数,

myRepl 1 (x:xs) y = (y:xs)
myRepl n (x:xs) y | n > 1 = x : myRepl ...

现在你可以完成剩下的工作了。

最后,Haskell是一种懒惰的语言。这意味着它最终只会调用所需的列表元素。如果你更换第7个元素怎么办,但后来只要求前3个?使用split的代码实际上会要求7个元素,因此在以后要求它们时它可以返回前3个元素。

现在,您希望以嵌套方式替换,并且用替换旧值的值取决于旧值newVal = let (a,b,ls)=oldVal in (a,b,myRepl p ls newtuple)。所以你确实需要使用函数而不是值重写(以便之前使用yconst y会去):

myUpd 1 (x:xs) f = (f x:xs)
myUpd n ... = ...

,您的整个通话都会变为myUpd n xxs (\(a,b,c)->(a,b,myUpd ... (const ...) ))

答案 2 :(得分:4)

首先,我们需要一个通用函数来映射列表的某个元素,例如:

mapN :: (a -> a) -> Int -> [a] -> [a] 
mapN f index list = zipWith replace list [1..] where
  replace x i | i == index = f x 
              | otherwise = x 

对于外部列表和内部列表,我们可以使用此功能两次。由于内部列表是元组的一部分,因此有一点复杂,所以我们需要另一个辅助函数:

mapTuple3 :: (c -> c) -> (a,b,c) -> (a,b,c)
mapTuple3 f (x,y,z) = (x,y,f z) 

现在我们已经拥有了将replace函数应用于我们的用例所需的一切:

fooo :: Int -> Int -> (Int,Int,Int) -> [(Int,Int,[(Int,Int,Int)])]
fooo n p newTuple = mapN (mapTuple3 (mapN (const newTuple) p)) n xxs

当然在内部列表中,我们不需要考虑旧值,因此我们可以使用const :: a -> (b -> a)来忽略该参数。