2011-03-19 28 views
7

Tôi đang cố gắng để thực hiện một chức năng tương đối đơn giản đó là gần như concat nhưng với một chút xoắn. Đó là nghĩa vụ phải nhị phân hoặc cùng nhau các yếu tố cuối cùng và đầu tiên của mỗi danh sách và kết hợp chúng trong quá trình này. Tôi đang nghiên cứu để viết mã có thể tận dụng khả năng của Stream Fusion trong Data.List.StreamTrong Haskell, concat xây dựng một danh sách uể oải nhưng phiên bản của riêng tôi với một twist, không

Tôi đã kiểm tra rằng concat trong cơ sở thực hiện những gì cần và xây dựng danh sách một cách lười biếng, tuy nhiên, phiên bản này tôi không được tạo. tại các cơ sở, concat được quy định như sau:

concat :: [[a]] -> [a] 
concat = foldr (++) [] 

(++) :: [a] -> [a] -> [a] 
(++) []  ys = ys 
(++) (x:xs) ys = x : xs ++ ys 

Dưới đây là mã của tôi:

bconcat :: [[Word8]] -> [Word8] 
bconcat = foldr1 bappend 

bappend :: [Word8] -> [Word8] -> [Word8] 
bappend as bs = init as ++ (last as .|. head bs) : tail bs 

Các câu hỏi tôi có được, làm thế nào để tôi viết những dòng này để danh sách được xây dựng một cách lười biếng? Tôi thậm chí đã cố gắng viết bappend bằng cách bắt chước định nghĩa (++) trong cơ sở nhưng nó không tạo ra sự khác biệt.

Hiện tại, tôi sử dụng mã sau đây, nó hoạt động theo cách tôi muốn nhưng hiệu suất tụt lại phía sau concat. Ngoài ra, nó sử dụng đệ quy rõ ràng mà tôi muốn tránh.

bconcat :: [[Word8]] -> [Word8] 
bconcat (a:b:xs) = init a ++ bconcat ((bappend (last a) b):xs) 
bconcat (a:[]) = a 
bconcat [] = [] 

bappend :: Word8 -> [Word8] -> [Word8] 
bappend !a bs = (a .|. head bs) : tail bs 

Vì vậy, câu hỏi tôi có là, làm thế nào để viết mã này để nó xây dựng danh sách một cách lười biếng và không có đệ quy rõ ràng?

Cảm ơn bạn đã dành thời gian.

Edit:

quan tâm chính của tôi, cho thời điểm này, là làm cho mã sạch, súc tích và understable với combinators chuẩn. Tôi vẫn còn rất nhiều người mới bắt đầu với tư duy chức năng và rất thích nhìn thấy một cách hiệu quả để đưa họ vào sử dụng ở đây.

+0

Tôi đoán rằng, vấn đề là, bạn phải đánh giá toàn bộ danh sách đầu tiên trước khi có thể hoặc cùng nhau. IMHO không có đánh giá lười biếng có thể. – fuz

+0

Có chắc chắn là một cách để làm cho nó xây dựng danh sách kết quả uể oải. concat thành công ở đó và nó gần như giống nhau. Ngoài ra, đoạn trích cuối cùng tôi đăng không làm theo cách tôi muốn. Nó chỉ không đạt đến mức hiệu suất của concat. – Elriel

+0

Tôi thấy sự khác biệt, phiên bản OR đầu tiên (cuối cùng) với số –

Trả lời

4

Định nghĩa của bạn trông nghiêm khắc với tôi. Ví dụ: hãy thử đánh giá

length $ bconcat [[1,2,3],[4,undefined,6]] 

Nhưng bạn có thể xây dựng các khối cho. |. biểu hiện. Có lẽ bạn muốn ép buộc điều đó.

Bạn luôn có thể fuse thứ cho mình nếu họ không cầu chì cũng tự động:

bconcat [] = error "bconcat: empty outer list" 
bconcat (xs:xss) = loop xss xs 
    where loop [] ys = ys 
     loop ((x:xs):xss) [y] = let z = x .|. y in seq z $ z : loop xss xs 
     loop ([]:_) _ = error "bconcat: empty inner list" 
     loop xss (y:ys) = y : loop xss ys 
+0

Tôi không chắc chắn định nghĩa nào của bconcat bạn muốn thử "length $ bconcat [[1,2,3], [4, undefined, 6]]" với nhưng nó trả về 5 trong cả hai trường hợp, như tôi mong đợi . – Elriel

+0

Cảm ơn bạn đã phiên bản hợp nhất của bconcat. Mã đó thực sự bay. Nó thậm chí còn nhanh hơn cả concat thông thường. Chương trình tôi đang cố gắng tối ưu hóa chạy nhanh hơn 40% với chương trình đó. – Elriel

+0

Ack, bị hỏng: "bconcat [[1], [2], [4]]" cho "[3 *** Ngoại lệ: /tmp/foo.hs:(5,9)-(8,41) : Các mẫu không đầy đủ trong vòng lặp chức năng " –

2

Bạn dường như có lỗi đánh máy, 'bapennd' phải là 'bappend' trong đoạn đầu tiên. Và tôi nghĩ rằng 'kết hợp' trong đoạn cuối nên là 'bconcat'.

Nó trông giống như lần đầu tiên (foldr1 bappend) sẽ khá lười biếng. Bạn có thể giải thích về sự thiếu lười biếng không?

Đối cần cả "init xs" và "xs cuối cùng" cùng một lúc, bạn có thể muốn có một traversal duy nhất của xs:

let un [] = error "no last element" 
    un (x:[]) = ([],x) 
    un (x:xs) = let (ys,z) = un xs 
       in (x:ys,z) 

này có một không gian và thời gian khác nhau trade-off. Nếu bạn luôn luôn buộc câu trả lời của bconcat theo thứ tự thì đó có thể là một sự cải tiến. "Các xs cuối cùng" giữ một tham chiếu đến phần đầu của toàn bộ danh sách và ngăn xs không bị thu gom rác cho đến khi nó bị ép buộc.

+0

Cảm ơn bạn, tôi đã khắc phục lỗi chính tả. thiếu lười biếng như trong việc ăn tất cả bộ nhớ trong hệ thống của tôi, gây ra bởi phải làm cho toàn bộ chuỗi trong bộ nhớ trước khi bất kỳ mã nào khác có thể làm bất cứ điều gì với nó. Toàn bộ ý tưởng là để chương trình này hoạt động trong bộ nhớ liên tục. – Elriel

+0

Tôi đã thử sử dụng chức năng của bạn để lấy đầu và đuôi cùng một lúc nhưng nó đã làm chậm mã. Tôi nghi ngờ điều này có một cái gì đó để làm với tối ưu hóa dòng chảy fusion như tôi đang sử dụng đó. Điều đó "xs cuối cùng" giữ tham chiếu không giải thích tại sao tôi nhận được một cải tiến nhỏ trong hiệu suất để làm cho tham số đầu tiên cho bappend tôi đang sử dụng hiện tại, nghiêm ngặt. – Elriel

2

tôi có thể thay đổi câu trả lời bằng cách augustuss để phù hợp chặt chẽ hơn vấn đề ban đầu bằng cách viết:

bconcat2 [] = [] 
bconcat2 (xs:xss) = loop xss xs 
    where loop [] ys = ys 
     loop xss (y:[]) = gather xss y 
     loop xss (y:ys) = y : loop xss ys 
     loop (xs:xss) [] = loop xss xs 
     gather [] z = [z] 
     gather ([]:xss) z = gather xss z 
     gather ((x:[]):xss) z = gather xss $! z .|. x 
     gather ((x:xs):xss) z = let z' = z .|. x in z' : loop xss xs 

Ở trên bỏ qua bất kỳ danh sách nội bộ trống nào.

+0

Cảm ơn bạn, mã hiện tại của tôi không cần xử lý các danh sách nội bộ trống nhưng nó có thể hữu ích cho một số mục đích khác. – Elriel

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