2012-02-17 60 views
11

tôi thấy mình thường viết mã sau mô hình:Cách để chức năng [a] -> [a] hoạt động trên [(a, Int)]?

foo xs = map snd $ filter ((< 10).fst) $ zip xs [0..] 

bar ys = map snd $ sortBy (compare `on` fst) $ zip ys [0..] 

Bây giờ tôi muốn trừu tượng này đi

foo = indexesOf (filter (<10)) 

bar = indexesOf sort 

indexesOf :: ([a] -> [a]) -> [a] -> [Int] 
indexesOf f xs = map snd $ magick $ zip xs [0..] where 
    magick = undefined 

Làm thế nào để thực hiện các magick?

+0

... Tôi thực sự không rõ ràng những gì bạn đang cố gắng làm. –

+0

Tôi có một hàm hoạt động trên danh sách, nói 'sắp xếp'. Bây giờ thay vì danh sách được sắp xếp, tôi muốn lấy lại các chỉ mục * của các phần tử. Ví dụ. cho '[5.0, 8.0, 7.0]' Tôi không muốn '[5.0. 7,0, 8,0] 'nhưng' [0,2,1] '. – Landei

Trả lời

11

Chữ ký loại của bạn sẽ không hoạt động. Bạn cần có khả năng cung cấp cho hàm được truyền một danh sách các bộ dữ liệu, có nghĩa là bạn phải sử dụng các loại xếp hạng cao hơn để buộc nó phải được đa hình hoặc đề cập rõ ràng các bộ dữ liệu trong chữ ký loại của bạn.

Nếu không có điều này, bạn không thể "nhìn vào bên trong" hàm để xem cách sắp xếp lại các yếu tố trong danh sách. Trong thực tế, với chữ ký kiểu của bạn, hàm được truyền có thể làm bất cứ điều gì nó muốn trong danh sách, bao gồm chèn các phần tử thậm chí không có trong đó để bắt đầu!

Đây là những gì tôi đã làm việc sử dụng các loại-rank cao hơn:

{-# LANGUAGE RankNTypes #-} 

import Data.List (sortBy) 
import Data.Ord (comparing) 

indexesOf :: (forall b. (b -> a) -> [b] -> [b]) -> [a] -> [Int] 
indexesOf f xs = map snd $ f fst $ zip xs [0..] 

foo :: (Ord a, Num a) => [a] -> [Int] 
foo = indexesOf (filter . ((< 10) .)) 

bar :: Ord a => [a] -> [Int] 
bar = indexesOf (sortBy . comparing) 

Lưu ý rằng tôi cũng đã có thêm một đối số bổ sung cho các chức năng thông qua nói với nó như thế nào để trích xuất các phần nó quan tâm đến từ các yếu tố của danh sách mà nó đang hoạt động. Nếu không có điều này, bạn sẽ chỉ có thể sử dụng các chức năng không kiểm tra các phần tử của danh sách, chẳng hạn như reverse và điều đó sẽ không hữu ích lắm.

Ví dụ chạy trong GHCi:

> let xs = [42, 0, 7, 3, 12, 17, 99, 36, 8] 
> foo xs 
[1,2,3,8] 
> bar xs 
[1,3,2,8,4,5,7,0,6] 
> indexesOf (const reverse) xs 
[8,7,6,5,4,3,2,1,0] 
+0

Tôi nghĩ bạn đang trả lời câu hỏi trong tiêu đề. –

+0

(tiếp tục) ... nhưng trong các ví dụ đã cho, hàm này thực sự là ':: (Ord a) => [a] -> [a]'. Vì vậy, bạn có thể "nhìn vào bên trong". –

+0

@RafaelCaetano: Bạn vẫn cần có khả năng chọn một 'a' khác, hoặc thông qua loại xếp hạng cao hơn hoặc bằng cách đề cập rõ ràng các bộ dữ liệu trong chữ ký loại. Bạn _could_ làm cho chữ ký kiểu 'indexOf :: (forall b. Ord b => [b] -> [b]) -> [a] -> [Int]', tuy nhiên điều đó sẽ chỉ làm việc cho ví dụ 'sort' và không phải là một với 'bộ lọc', vì hàm này chỉ có thể so sánh các phần tử với nhau. Nó sẽ không thể so sánh với '10'. – hammar

4

Great câu hỏi, nhưng tôi nghi ngờ có tồn tại không có chức năng như vậy. Xem Theorems For Free!. Giống như búa nói, bạn cần phải vượt qua các hàm nhận các bộ dữ liệu một cách rõ ràng.

Đây là phiên bản của tôi hơi đơn giản mà không đòi hỏi RankNTypes (đó là, phải thừa nhận, không phải là một cải tiến rất tốt trên mã cũ):

import Data.List 
import Data.Ord 

indexesOf :: ([(a,Int)] -> [(a,Int)]) -> [a] -> [Int] 
indexesOf f xs = map snd $ f $ zip xs [0..] 

foo :: (Ord a,Num a) => [a] -> [Int] 
foo = indexesOf $ filter $ (< 10) . fst 

bar :: Ord a => [a] -> [Int] 
bar = indexesOf $ sortBy $ comparing fst 
+1

Điều này là đúng, mặc dù các loại xếp hạng cao hơn cung cấp cho bạn đảm bảo mạnh hơn một chút vì chức năng không thể gây rối với các số nguyên. Tất cả những gì nó có thể làm là sắp xếp lại, sao chép và xóa các mục hiện có trong danh sách. – hammar

+0

Đó là sự thật. Ngoài ra, khi một hàm có kiểu a -> Int, nó phải là tầm thường, nghĩa là một hằng số (hoặc không xác định). – mnish

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