2011-12-13 32 views
8

Tôi có danh sách sau đây (đó là một danh sách dài 2, nhưng trong nhiệm vụ của tôi, tôi có một chiều dài + n danh sách)“thay thế” 3-tuple

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

Tôi đang cố gắng để “thay thế” một 3-tuple (p1 hoặc p2 hoặc p3 hoặc p4 từ hình bên dưới) theo chỉ mục danh sách (n) và theo chỉ mục phụ danh sách (p).

Visual of list breakdown

Chức năng, ở cuối, nên như thế:

fooo newtuple n p = (…) 

Ví dụ: (thay thế p3 for (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)])] 

tôi đã lập kế hoạch mã như sau các bước này:

  1. Truy cập vào pn mà tôi muốn thay đổi. Tôi quản lý để đạt được điều đó bằng cách:

    fob n p = ((aux2 xxs)!!n)!!p 
        where aux2 [] = [] 
         aux2 ((_,_,c):xs) = c:aux2 xs 
    
  2. "thay thế" 3 bộ. Tôi rất cần một sự trợ giúp ở đây. Tôi bị kẹt. các mã tốt nhất (trong đầu của tôi nó làm cho một số ý nghĩa) mà tôi đã làm: (nhớ: xin vui lòng không quá xấu trên mã của tôi, tôi đã chỉ được nghiên cứu Haskell chỉ trong 5 tuần)

    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. Cuối cùng tôi sẽ đặt tất cả lại với nhau, sử dụng splitAt.

Cách tiếp cận của tôi có khắc phục được sự cố không? Tôi thực sự sẽ đánh giá cao một số trợ giúp về bước 2.

Trả lời

7

Tôi cũng hơi mới với Haskell, nhưng hãy xem liệu chúng tôi có thể tìm ra cách làm tốt hay không.

Vì vậy, về cơ bản những gì chúng tôi đang cố gắng làm là sửa đổi điều gì đó trong danh sách. Sử dụng lập trình hàm Tôi muốn giữ nó một chút chung chung, vì vậy hãy tạo một hàm update.

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

Đó bây giờ sẽ có một chỉ số, một chức năng và một danh sách và thay thế các yếu tố nth trong danh sách với kết quả của hàm được áp dụng cho nó.

Tuy nhiên, trong vấn đề lớn hơn của chúng tôi, chúng tôi cần cập nhật trong ngữ cảnh lồng nhau. May mắn, chức năng update của chúng tôi có chức năng như một đối số, vì vậy chúng tôi cũng có thể gọi số update trong đó!

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 

Tất cả fooo phải làm là cập nhật gọi hai lần (một lần trong cuộc gọi khác) và làm một chút "housekeeping" làm việc (đặt kết quả trong tuple chính xác). (n-1)(p-1) là vì bạn có vẻ đang lập chỉ mục bắt đầu từ 1, trong khi Haskell bắt đầu tại 0.

Cho phép chỉ thấy nếu làm việc với trường hợp thử nghiệm của chúng tôi:

*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

Trước hết, lời giải thích của bạn thật đáng chú ý.Thứ hai: Tôi vẫn còn nhiều điều để học, nhưng tôi đã học được rất nhiều câu trả lời của bạn. Thứ ba: Tôi dành một nửa thời gian của mình để giải quyết vấn đề này và bạn sẽ giải quyết nó sau vài phút (tôi thực sự có nhiều điều để học và học). Cuối cùng, cảm ơn BIG! _o_ – Nomics

4

Vì vậy, bạn đã cố gắng sử dụng một số chức năng làm sẵn, (!!). Nó có thể truy cập một mục trong danh sách cho bạn, nhưng quên vị trí của nó ở đó, vì vậy không thể cập nhật. Bạn đã có một giải pháp được cung cấp, sử dụng một hàm sẵn sàng khác được thực hiện split, làm rơi một danh sách thành hai phần, và (++) kết dính chúng lại thành một.

Nhưng để có được cảm giác thực sự, điều tôi nghi ngờ nhiệm vụ của bạn là nhắm vào vị trí đầu tiên (thật dễ dàng để quên tên chức năng và dễ dàng viết cho bạn một tên mới), bạn có thể thử viết cái đầu tiên, (!!), chính bạn. Sau đó, bạn sẽ thấy thật dễ dàng để sửa đổi nó để nó có thể cập nhật danh sách quá.

Để viết chức năng của bạn, nghĩ tốt nhất của nó như là một phương trình tương đương:

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

khi n là zero, chúng tôi chỉ lấy đi các yếu tố đầu. Chúng ta làm gì khi nó không? Chúng tôi cố gắng tiến gần hơn về phía không. Bạn có thể điền vào chỗ trống.

Vì vậy, ở đây chúng tôi đã trả về phần tử được tìm thấy. Nếu chúng ta muốn thay thế nó thì sao? Thay thế nó bằng cái gì? - điều này gọi một tham số khác vào sự tồn tại,

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

Bây giờ bạn có thể hoàn thành phần còn lại, tôi nghĩ vậy.

Cuối cùng, Haskell là một ngôn ngữ lười biếng. Điều đó có nghĩa là nó chỉ kêu gọi tồn tại các yếu tố của một danh sách cần thiết, cuối cùng. Điều gì xảy ra nếu bạn thay thế phần tử thứ 7, nhưng chỉ 3 yêu cầu đầu tiên sau đó được yêu cầu? Mã sử ​​dụng split sẽ thực sự yêu cầu 7 yếu tố, vì vậy nó có thể trả về 3 phần tử đầu tiên khi được yêu cầu.

Bây giờ trong trường hợp bạn muốn thay thế theo kiểu lồng nhau và giá trị để thay thế hình cũ bằng phụ thuộc vào giá trị cũ: newVal = let (a,b,ls)=oldVal in (a,b,myRepl p ls newtuple). Vì vậy, thực sự bạn cần phải viết lại sử dụng chức năng thay vì giá trị (để nơi y đã được sử dụng trước đó, const y sẽ đi):

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

và toàn bộ cuộc gọi của bạn trở nên myUpd n xxs (\(a,b,c)->(a,b,myUpd ... (const ...))).

+0

Đó là giải pháp gần nhất với giải pháp mà tôi đã thử. Tôi sẽ cố gắng hoàn thành (...). Lời giải thích là tất cả ở đó. :) Cảm ơn bạn!! – Nomics

+1

@Nomics ý của tôi là viết chức năng của bạn, nghĩ về nó như thể bạn đã viết nó, như thể bạn đã sẵn sàng để sử dụng, và chỉ viết một số phương trình tương đương dựa trên những phẩm chất mà nó phải có, pháp luật nó phải tuân theo. Những phương trình này sẽ trở thành định nghĩa của bản thân hàm - miễn là bạn tuân theo luật của nó, giữ các bất biến trước và sau khi sử dụng nó, ví dụ: _danh sách với elt thứ nhất thay thế chỉ là vậy; danh sách với _n_th elt thay thế là elt đầu tiên được đặt trước phần còn lại của nó với _ (n-1) _- th elt replace._ Đúng vậy. –

4

Đầu tiên, chúng ta cần một hàm tổng quát để ánh xạ một yếu tố nào đó của một danh sách, ví dụ .:

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

Chúng ta có thể sử dụng chức năng này hai lần, để xem danh sách bên ngoài và danh sách bên trong. Có một chút phức tạp như danh sách bên trong là một phần của một tuple, vì vậy chúng tôi cần một chức năng helper:

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

Bây giờ chúng tôi có tất cả mọi thứ chúng ta cần phải áp dụng các chức năng thay thế cho trường hợp sử dụng của chúng tôi:

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

Tất nhiên trong danh sách bên trong, chúng ta không cần phải xem xét giá trị cũ, vì vậy chúng ta có thể sử dụng const :: a -> (b -> a) để bỏ qua đối số đó.