2012-03-19 59 views
5

im tìm kiếm giải pháp cho lớp Haskell của tôi.Tách danh sách và kiếm tiền từ danh sách con?

Tôi có danh sách các số và tôi cần trả về SUM cho mọi phần của danh sách. Các bộ phận được chia cho 0. Tôi cần sử dụng hàm FOLDL.

Ví dụ:
danh sách ban đầu: [1,2,3,0,3,4,0,5,2,1]
sublist [[1,2,3], [3,4], [5,2,1]]
kết quả [6,7,7]

tôi có một chức năng cho việc tìm kiếm 0 trong danh sách ban đầu:

findPos list = [index+1 | (index, e) <- zip [0..] list, e == 0] 

(lợi nhuận [4,6] cho danh sách ban đầu ví dụ)

và chức năng tạo SUM với FOLDL:

sumList list = foldl (+) 0 list 

Nhưng tôi hoàn toàn thất bại trong việc đặt nó với nhau:/

---- GIẢI PHÁP CỦA TÔI
Cuối cùng tôi tìm thấy một cái gì đó hoàn toàn khác nhau mà các bạn đề nghị.
Đã cho tôi cả ngày để làm cho nó:/

groups :: [Int] -> [Int] 
groups list = [sum x | x <- makelist list] 

makelist :: [Int] -> [[Int]] 
makelist xs = reverse (foldl (\acc x -> zero x acc) [[]] xs) 

zero :: Int -> [[Int]] -> [[Int]] 
zero x acc | x == 0 = addnewtolist acc 
      | otherwise = addtolist x acc 

addtolist :: Int -> [[Int]] -> [[Int]] 
addtolist i listlist = (i : (head listlist)) : (drop 1 listlist) 

addnewtolist :: [[Int]] -> [[Int]] 
addnewtolist listlist = [] : listlist 
+0

Tôi nghĩ 'kết quả' phải là' [6,7,8] 'thay vì' [6,7,7] '. – Landei

Trả lời

2

Bây giờ bạn đã hoàn thành sự cố của riêng mình, tôi cho bạn thấy phiên bản tiết kiệm hơn một chút. Foldr có vẻ tốt hơn theo ý kiến ​​của tôi đối với vấn đề này *, nhưng vì bạn đã yêu cầu foldl Tôi sẽ chỉ cho bạn giải pháp của tôi bằng cả hai chức năng.

Ngoài ra, ví dụ của bạn dường như là không chính xác, tổng của [5,2,1] là 8, không 7.

Phiên bản foldr.

makelist' l = foldr (\x (n:ns) -> if x == 0 then 0:(n:ns) else (x + n):ns) [0] l 

Trong phiên bản này, chúng tôi đi qua danh sách, nếu các yếu tố hiện tại (x) là một 0, chúng tôi thêm một yếu tố mới vào danh sách ắc (n: ns). Nếu không, chúng ta thêm giá trị của phần tử hiện tại vào giá trị của phần tử phía trước của bộ tích lũy và thay thế giá trị trước của bộ tích lũy bằng giá trị này.

Từng bước:

  1. acc = [0], x = 1. Result is [0+1]
  2. acc = [1], x = 2. Result is [1+2]
  3. acc = [3], x = 5. Result is [3+5]
  4. acc = [8], x = 0. Result is 0:[8]
  5. acc = [0,8], x = 4. Result is [0+4,8]
  6. acc = [4,8], x = 3. Result is [4+3,8]
  7. acc = [7,8], x = 0. Result is 0:[7,8]
  8. acc = [0,7,8], x = 3. Result is [0+3,7,8]
  9. acc = [3,7,8], x = 2. Result is [3+2,7,8]
  10. acc = [5,7,8], x = 1. Result is [5+1,7,8] = [6,7,8]

Ở đó bạn có nó!

Và phiên bản foldl. Hoạt động tương tự như trên, nhưng tạo ra một danh sách đảo ngược, do đó việc sử dụng đảo ngược ở đầu hàm này để hủy bỏ danh sách.

makelist l = reverse $ foldl (\(n:ns) x -> if x == 0 then 0:(n:ns) else (x + n):ns) [0] l 

* Gấp danh sách từ bên phải cho phép hàm cons (:) được sử dụng một cách tự nhiên, sử dụng danh sách đảo ngược. (Có thể là một cách đơn giản hơn để làm phiên bản lần trái nhưng tôi không nghĩ rằng loại bỏ tầm thường này.)

+0

tuyệt vời, cảm ơn bạn :) – m4recek

6

tôi sẽ cung cấp cho bạn một số gợi ý, chứ không phải là một giải pháp hoàn chỉnh, vì điều này có vẻ như nó có thể là một bài tập về nhà.

Tôi thích phân tích các bước bạn đã đề xuất. Đối với bước đầu tiên (đi từ một danh sách các số có dấu 0 đến một danh sách các danh sách), tôi đề nghị thực hiện một đệ quy rõ ràng; hãy thử mẫu này cho một mẫu:

splits []  = {- ... -} 
splits (0:xs) = {- ... -} 
splits (x:xs) = {- ... -} 

Bạn cũng có thể lạm dụng groupBy nếu bạn cẩn thận.

Đối với bước thứ hai, có vẻ như bạn sắp hoàn tất; bước cuối cùng bạn cần là xem xét hàm map :: (a -> b) -> ([a] -> [b]), có chức năng bình thường và chạy nó trên mỗi phần tử của danh sách.

Là một bài tập tiền thưởng, bạn có thể muốn suy nghĩ về cách bạn có thể làm toàn bộ điều trong một lần như một lần duy nhất. Có thể - và thậm chí không quá khó, nếu bạn theo dõi những loại đối số khác nhau với foldr/foldl sẽ phải như thế!

bổ sung kể từ khi câu hỏi đã thay đổi:

Kể từ khi có vẻ như bạn đã làm việc ra một giải pháp, bây giờ tôi cảm thấy thoải mái đưa ra một số spoilers. =)

Tôi đã đề xuất hai triển khai có thể có; một bước đi từng bước, như bạn đã đề xuất và một bước khác đi cùng một lúc.Bước theo bước người ta có thể trông như thế này:

splits []  = [] 
splits (0:xs) = [] : splits xs 
splits (x:xs) = case splits xs of 
    []  -> [[x]] 
    (ys:yss) -> ((x:ys):yss) 

groups' = map sum . splits 

Hoặc như thế này:

splits' = groupBy (\x y -> y /= 0) 
groups'' = map sum . splits' 

Các all-at-once phiên bản có thể trông như thế này:

accumulate 0 xs  = 0:xs 
accumulate n (x:xs) = (n+x):xs 

groups''' = foldr accumulate [0] 

Để hãy kiểm tra xem bạn có hiểu những điều này hay không, dưới đây là một số bài tập bạn có thể muốn thử:

  • Điều gì làm splitssplits' làm với [1,2,3,0,4,5]? [1,2,0,3,4,0]? [0]? []? Kiểm tra các dự đoán của bạn trong ghci.
  • Dự đoán mỗi phiên bản của groups (bao gồm cả của bạn) đầu ra cho các đầu vào như [] hoặc [1,2,0,3,4,0] và sau đó kiểm tra dự đoán của bạn trong ghci.
  • Sửa đổi groups''' để thể hiện hành vi của một trong các triển khai khác.
  • Sửa đổi groups''' để sử dụng foldl thay vì foldr.
+0

Và bạn có thể sử dụng gói tách từ Hackage và 'splitOn [0]' nếu đây không phải là bài tập về nhà. – Jedai

+0

Xin chào, cảm ơn bạn đã giúp đỡ :) Đáng buồn thay, nó không hữu ích cho tôi. Tôi khách mà tôi cần "ví dụ cho núm vú giả". Im thực sự mới để haskell vì vậy tôi đã có vấn đề như: "làm thế nào để thêm một cái gì đó vào danh sách đầu tiên trong danh sách" và "làm thế nào để viết các loại" vv .. anyway, bạn có thể vui lòng viết giải pháp bạn đề nghị? Im quan tâm phải như thế nào :) – m4recek

+0

@ user1279158 Xong. –

1

Như bạn đã giải quyết nó, một phiên bản khác:

subListSums list = reverse $ foldl subSum [0] list where 
    subSum xs 0 = 0 : xs 
    subSum (x:xs) n = (x+n) : xs 

(Giả sử bạn chỉ có số không âm trong danh sách)

+0

Giả định không phủ định xuất hiện ở đâu trong mã? –

+0

Nếu các số của một nhóm sẽ tổng hợp bằng không, số 0 này sẽ bị "ghi đè" bởi nhóm tiếp theo, vì vậy tổng này sẽ không xuất hiện trong danh sách cuối cùng. – Landei

+0

Tôi nghĩ rằng bạn có thể đã hiểu lầm mã của riêng bạn! Không có phần nào trong mã của bạn kiểm tra xem tổng chạy hiện tại có bằng '0' hay không; hơn nữa, trên một mức độ thực nghiệm hơn, 'subListSums [2, -2,0,2, -2]' dường như làm điều đúng trong ghci. –

Các vấn đề liên quan