Tôi muốn chuyển đổi [1,2,3,4]
thành [[1 2] [2 3] [3 4]]
hoặc [(1 2) (2 3) (3 4)]
. Trong clojure tôi có (partition 2 1 [1,2,3,4])
. Làm thế nào tôi có thể làm điều đó trong haskell? Tôi nghi ngờ có chức năng như vậy trong api tiêu chuẩn nhưng tôi không thể tìm thấy nó.Các thành phần trong danh sách
Trả lời
Bí quyết tiêu chuẩn cho điều này là để zip
danh sách với riêng của nó tail
:
> let xs = [1,2,3,4] in zip xs (tail xs)
[(1,2),(2,3),(3,4)]
Để xem lý do tại sao các công trình này, xếp hàng lên danh sách và đuôi của nó bằng mắt thường.
xs = 1 : 2 : 3 : 4 : []
tail xs = 2 : 3 : 4 : []
và lưu ý rằng zip
đang tạo tuple ra khỏi mỗi cột.
Có hai lý do tế nhị hơn tại sao điều này luôn luôn làm điều đúng đắn:
zip
dừng khi một trong hai danh sách chạy ra khỏi các yếu tố. Điều đó có ý nghĩa ở đây vì chúng ta không thể có một cặp "không hoàn chỉnh" ở cuối và nó cũng đảm bảo rằng chúng ta không nhận được cặp nào từ một danh sách phần tử đơn lẻ.- Khi
xs
trống, người ta có thể mong đợitail xs
để ném ngoại lệ. Tuy nhiên, vìzip
kiểm tra đối số đầu tiên của nó trước tiên, khi nó thấy rằng đó là danh sách trống, đối số thứ hai không bao giờ được đánh giá.
Mọi thứ ở trên cũng đúng với zipWith
, vì vậy bạn có thể sử dụng cùng một phương pháp bất cứ khi nào bạn cần áp dụng chức năng theo từng cặp cho các yếu tố lân cận.
Đối với một giải pháp chung như Clojure's partition
, không có gì trong thư viện chuẩn. Tuy nhiên, bạn có thể thử một cái gì đó như thế này:
partition' :: Int -> Int -> [a] -> [[a]]
partition' size offset
| size <= 0 = error "partition': size must be positive"
| offset <= 0 = error "partition': offset must be positive"
| otherwise = loop
where
loop :: [a] -> [[a]]
loop xs = case splitAt size xs of
-- If the second part is empty, we're at the end. But we might
-- have gotten less than we asked for, hence the check.
(ys, []) -> if length ys == size then [ys] else []
(ys, _) -> ys : loop (drop offset xs)
vì vậy không có chức năng chung cho nó trong thư viện chuẩn ?, nếu muốn không có bộ ba mà là bộ ba hoặc nếu tôi muốn lặp lại hoặc bỏ qua một số giá trị giống như http://clojuredocs.org/clojure_core/clojure.core/partition – piotrek
@piotrek: Không có gì trong thư viện chuẩn, không. Tuy nhiên, không khó để triển khai. Tôi đã thêm triển khai mẫu vào câu trả lời của mình – hammar
Chỉ cần ném một câu trả lời ra có sử dụng một cách tiếp cận khác nhau:
Đối với n = 2 bạn muốn chỉ đơn giản zip
danh sách với đuôi của nó. Đối với n = 3 bạn muốn nén danh sách với đuôi của nó và đuôi của đuôi của nó. mô hình này tiếp tục hơn nữa, vì vậy tất cả chúng ta phải làm là khái quát nó:
partition n = sequence . take n . iterate tail
Nhưng điều này chỉ làm việc cho một bù đắp của 1. Để khái quát offsets chúng tôi chỉ phải nhìn vào danh sách genrated. Biểu mẫu sẽ luôn có dạng:
[[1..something],[2..something+1],..]
Vì vậy, tất cả việc cần làm là chọn mọi thành phần thứ offset
và chúng ta nên ổn. Tôi shamelessy đánh cắp phiên bản này từ @ertes từ this question:
everyNth :: Int -> [a] -> [a]
everyNth n = map head . takeWhile (not . null) . iterate (drop n)
Toàn bộ chức năng lúc này là:
partition size offset = everyNth offset . sequence . take size . iterate tail
Thật không may là sự khái quát hóa của bạn mang lại một số hoán vị lạ – flq
Tại sao clojure gọi này 'partition'? Nếu bạn phân vùng một cái gì đó, bạn phân chia theo cách mà nó là tổng của các bộ phận. – AndrewC
Oh - tìm thấy [tài liệu đinh ghim] (http://clojuredocs.org/clojure_core/clojure.core/partition) mô tả "phân đoạn n bước coll: Trả về một chuỗi các danh sách gồm n mục mỗi lần, tại các bước tắt, cách nhau .Nếu bước không được cung cấp, mặc định là n, tức là các phân vùng không chồng lên nhau. " – AndrewC