2010-05-27 20 views

Trả lời

39

Trong một cách tổng quát, bạn không thể. Mỗi kích thước của tuple là một kiểu riêng biệt, trong khi các danh sách có độ dài bất kỳ là một kiểu duy nhất. Vì vậy, không có cách nào tốt để viết một hàm nhận danh sách và trả về một bộ tuple có cùng độ dài - nó sẽ không có kiểu trả về được xác định rõ.

Ví dụ, bạn có thể có các chức năng như:

tuplify2 :: [a] -> (a,a) 
tuplify2 [x,y] = (x,y) 

tuplify3 :: [a] -> (a,a,a) 
tuplify3 [x,y,z] = (x,y,z) 

... nhưng không phải một mà không được công việc của cả hai.

Bạn có thể viết phiên bản chung bằng nhiều loại lập trình meta khác nhau, nhưng bạn hiếm khi muốn.

Lưu ý rằng cùng một vấn đề áp dụng cho những thứ khác, chẳng hạn như viết các phiên bản lớp cho các bộ dữ liệu khác nhau - hãy xem the source code for Data.Tuple từ các thư viện chuẩn!

+0

Làm cách nào có thể là nguồn thực của 'Data.Tuple'? Ý tôi là ... phần tử thứ hai trong danh sách xuất khẩu là: ', snd - :: (a, b) -> a'. Đây có phải là lỗi rõ ràng trong các loại thực sự ở đó không? – Bakuriu

+2

@ Bakuriu: Ha, đó là một lỗi đánh máy thú vị. Nó chỉ là một bình luận, tất nhiên - chữ ký loại trên 'snd' gần cuối của tập tin là chính xác. Đó cũng là phiên bản khá cũ của 'Data.Tuple', các phiên bản gần đây không có lỗi đó. –

6

Tuples và danh sách là những điều rất khác nhau. Về điều tốt nhất bạn có thể làm là viết chức năng chuyển đổi theo cách thủ công:

toTuple :: [a] -> (a,a,a,a,a,a) 
toTuple [a,b,c,d,e,f] = (a,b,c,d,e,f) 

Lưu ý rằng các loại khác nhau là: biến duy nhất của danh sách mở rộng đến sáu biến cho bộ dữ liệu. Vì vậy, bạn sẽ cần một hàm cho mỗi kích thước của bộ tuple.

+1

Bạn và camccann đã đánh tôi. Đó là nó, tốt nhất anh ta có thể làm là N chức năng và sau đó một xử lý để gọi một trong những cho một N. – Spidey

+0

@Spidey nhất định: Hoặc là hoặc tìm hiểu làm thế nào để sử dụng mẫu Haskell! –

+0

tuples là danh sách trong toán học. chúng rất giống nhau. Trong việc thực hiện của Haskell, các bộ dữ liệu có độ dài và danh sách đã gõ không cố định, vì vậy không dễ để chuyển đổi hai, nhưng đó là vấn đề triển khai ngôn ngữ cụ thể. – clay

2

Bạn thực tế có thể làm tốt hơn việc viết một hàm theo kích thước tuple theo cách thủ công nếu bạn sử dụng dấu ngoặc kép [1]. Tuy nhiên, tôi sẽ tự hỏi về mã mà bạn mong muốn sử dụng một cách tổng quát.

[1] http://www.haskell.org/haskellwiki/Quasiquotation

+0

Hy vọng sẽ làm việc lên một ví dụ nhỏ sau ngày hôm nay khi tôi có thời gian và chỉnh sửa bình luận của tôi với nó ... – StevenC

+2

Quasiquotation là một phần của mẫu Haskell, tôi tin rằng, đó là meta-lập trình tôi đề cập đến. Cuối cùng, điều này vẫn tạo ra một hàm cho mỗi kích thước tuple, nó chỉ viết mã biên dịch để làm điều đó cho bạn thay vì viết chúng bằng tay. Tôi chưa nhận được hang của TH bản thân mình, vì vậy tôi mong được nhìn thấy ví dụ của bạn! –

+0

Edward Kmett đánh tôi với nó. Tôi nghĩ rằng tôi đã làm nó với quasiquotes trước, nhưng tôi đoán nó chỉ là TH. Lấy làm tiếc. – StevenC

23

Template Haskell là càng gần như bạn có thể có được do gõ kiểm tra nếu bạn muốn trích xuất một số biến của các yếu tố, kể từ (a, b) và (a, b, c) có các loại khác nhau.

{-# LANGUAGE TemplateHaskell #-} 
import Language.Haskell.TH 
tuple :: Int -> ExpQ 
tuple n = do 
    ns <- replicateM n (newName "x") 
    lamE [foldr (\x y -> conP '(:) [varP x,y]) wildP ns] (tupE $ map varE ns) 

Sau đó:

$(tuple 6) [1,2,3,4,5,6] == (1,2,3,4,5,6) 
$(tuple 3) "abc" == ('a','b','c') 

Nhưng nói chung, nếu bạn cần câu trả lời này, sau đó bạn đang đặt ra câu hỏi sai ở đâu đó.

Nếu bạn chỉ muốn truy cập ngẫu nhiên bằng phẳng, có lẽ tùy chọn tốt hơn sẽ là sử dụng một mảng.

+3

Mẫu Haskell thật tuyệt vời, nhưng tôi ước nó có thể đọc được một chút. Sau đó, một lần nữa, có lẽ nó là tốt nhất mà nó không phải là - nếu không mọi người sẽ sử dụng nó nhiều hơn họ thực sự cần. –

+0

Rõ ràng 'Control.Lens' thậm chí còn gần gũi hơn với' map (^ .. each) ', cũng hoạt động cho các bộ dữ liệu tùy ý. - Không phải bất cứ thứ gì là "cho các bộ dữ liệu tùy ý" đều có liên quan đến toàn bộ các bộ dữ liệu. (Đó là danh sách những gì. Tuples được sử dụng chính xác khi người ta muốn ngăn chặn kiểu sử dụng này.) – Evi1M4chine

+0

Do cách 'mỗi' sử dụng các mẫu lười: ' ghci> undefined & partsOf each. ~ [1,2, 3,4] :: (Int, Int, Int, Int) 'sản lượng (1,2,3,4). Vì vậy, bạn có thể đi cả hai chiều lên tới kích thước 9 hoặc hơn. Tất nhiên bạn cần phải biết loại kết quả tuple. –

3

Tôi không nghĩ rằng nó có thể làm điều này trong Haskell, cho một danh sách độ dài tùy ý không được biết tại thời gian biên dịch. Mẫu Haskell không thể làm được vì nó chỉ hoạt động ở thời gian biên dịch. Tôi chạy vào một trường hợp mà tôi cần phải làm chính xác điều này, và tôi đã phải làm việc xung quanh nó. Một thư viện cơ sở dữ liệu mong đợi các tuple có độ dài khác nhau cho các đối số truy vấn, nhưng tôi có một danh sách dài tùy ý. Vì vậy, tôi phải váy quanh giao diện thư viện. Sẽ rất tuyệt nếu nó có thể lấy danh sách.

Về cơ bản, vấn đề là các bộ nhớ có độ dài khác nhau là các loại khác nhau. Nhưng trình biên dịch Haskell phải biết ở dạng biên dịch các kiểu có thể tồn tại trong thời gian chạy. Việc chuyển đổi một danh sách dài tùy ý thành một tuple tại thời gian chạy có thể tạo ra một số kiểu mà nó không biết về thời gian biên dịch.

4

tôi cảm thấy khó khăn để làm cho lời giải thích thuyết phục của thao tác Template Haskell, nhưng đây là một minh chứng:

> :m +Language.Haskell.TH 
> :set -XTemplateHaskell 
> runQ [| [1,2,3,4,5,6] |] >>= putStrLn . pprint 
[1, 2, 3, 4, 5, 6] 
> runQ [| [1,2,3,4,5,6] |] >>= \ (ListE exps) -> putStrLn (pprint (TupE exps)) 
(1, 2, 3, 4, 5, 6) 
8

Nếu cảm giác như tôi sắp khuyên bạn nên chỉ súng vào chân của bạn và tin tưởng bạn không để bắn.

> list2Tuple lst = read $ "(" ++ (init.tail.show) lst ++ ")" 
> list2Tuple [1,2,3] :: (Int, Int, Int) 
(1,2,3) 
> list2Tuple [1,2,3,4] :: (Int, Int, Int, Int) 
(1,2,3,4) 

Điều này sẽ phù hợp với độ dài bao giờ của Hiển thị và Đọc được xác định.

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