2011-12-13 53 views
8

我有以下的列表(這是一個長2列,但在我的任務,我有一個長度+ N列表)「替換」 3元組

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

我想「取代」一3元組(p1或p2或圖像波紋管中的p3或p4)按列表索引(n)和子列表索引(p)。

Visual of list breakdown

的功能,在結束時,應該是這樣的:

fooo newtuple n p = (…) 

例如:(爲(98,98,98)替換P3:

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. 「替換」三元組。我真的需要一些幫助。我卡住了。 (我記得:請不要在我的代碼上太壞,我只在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

我的方法是否正確?我真的很感謝第2步的一些幫助。

回答

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)])] 
+0

首先,你的解釋是非常了不起的。第二:我還有很多東西需要學習,但是我對你的回答有了很多瞭解。第三:我花了一天的時間試圖解決這個問題,幾分鐘後就解決了(我真的有很多東西要學習和學習)。最後,大家謝謝! _o_ – Nomics

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)。因此,我們確實需要使用的功能而不是值(使這裏y是之前使用,const y會去)重新寫:

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

和你的整個通話變得myUpd n xxs (\(a,b,c)->(a,b,myUpd ... (const ...)))

+0

這是一個最接近我嘗試過的解決方案。我會盡力完成(...)。解釋就在那裏。 :) 謝謝!! – Nomics

+1

@Nomics我的意思是,編寫你的函數,把它看作你已經寫好了,就好像你已經可以使用它,並且根據它應該的特性寫下一些等價方程有,它應該遵循的法律。這些方程式將成爲函數本身的定義 - 只要遵循它的定律,在它的使用前後保持不變量,例如:_1代替的列表就是那個; _n_th elt替換的列表是第一個elt,其餘部分以_(n-1)_替換爲前綴._就是這樣。 –

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) 

現在,我們擁有一切,我們需要替換功能應用到我們的使用案例:

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)來忽略該參數。