2012-02-25 47 views
6

Tôi cần chạy một hàm lấy hai đối số nhiều lần. Tôi có hai danh sách chứa các đối số này và tôi muốn có thể sử dụng map hoặc một cái gì đó tương tự để gọi hàm với các arg tương ứng.Sử dụng bản đồ với hai danh sách thay vì một danh sách. Bạn có thể làm tổ không?

Chức năng tôi muốn gọi có kiểu này:

runParseTest :: String -> String -> IO() 

Các danh sách được tạo ra như thế này:

-- Get list of files in libraries directory 
files <- getDirectoryContents "tests/libraries" 
-- Filter out ".." and "." and add path 
let names = filter (\x -> head x /= '.') files 
let libs = ["tests/libraries/" ++ f | f <- names] 

Vì vậy, cho phép nói rằng names chứa ["test1.js", "test2.js", "test3.js"]libs chứa ["tests/libraries/test1.js", "tests/libraries/test2.js", "tests/libraries/test3.js"]

Tôi muốn gọi cho họ như sau:

runParseTest "test1.js" "tests/libraries/test1.js" 
runParseTest "test2.js" "tests/libraries/test2.js" 
runParseTest "test3.js" "tests/libraries/test3.js" 

Tôi biết tôi có thể tạo một hàm trợ giúp thực hiện điều này khá dễ dàng, nhưng không quan tâm, có thể thực hiện một dòng bằng cách sử dụng map không?

Đây là những gì tôi có cho đến nay, nhưng rõ ràng là đối số đầu tiên luôn luôn là "thử nghiệm":

mapM_ (runParseTest "test") libs 

Tôi xin lỗi nếu điều này là không rõ ràng. Tôi có thể cung cấp thêm thông tin nếu cần.

Trả lời

10

Vì vậy, cho phép nói rằng tên chứa ["test1.js", "test2.js", "test3.js"] và libs chứa ["tests/libraries/test1.js", "tests/libraries/test2.js", "tests/libraries/test3.js"]

tôi muốn gọi họ như thế này:

runParseTest "test1.js" "tests/libraries/test1.js" runParseTest "test2.js" "tests/libraries/test2.js" runParseTest "test3.js" "tests/libraries/test3.js"

Có thể để làm điều đó với zip:

map (\(a,b) -> runParseTest a b) $ zip names libs 

Hoặc có thể uncurry runParseTest:

map (uncurry runParseTest) $ zip names libs 

Hoặc với zipWith:

zipWith runParseTest names libs 

Và cũng giống như Ozgur nói, có một số tương tự cho monads:

> :t zipWithM 
zipWithM :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m [c] 
> :t zipWithM_ 
zipWithM_ :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m() 
+0

Đây là những gì tôi đang tìm kiếm. Tôi đã thực sự viết một chức năng mới gọi là 'map2M_' mà tôi sẽ đăng nhưng đây là cách" đúng "để làm điều đó tôi chắc chắn. –

3

Bạn đang tìm kiếm zipWithM_.

Bạn nói bạn có thể viết hàm trợ giúp thực hiện việc này. Điều này có nghĩa là bạn biết loại hàm bạn đang tìm kiếm. Trong những trường hợp như vậy, bạn có thể sử dụng hoogle.

(Hãy thử: Monad m => [a] -> [b] -> m())

15

Đây là một thời gian tuyệt vời để sử dụng Hoogle!Hoogle là một công cụ tìm kiếm để tìm kiếm Haskell các loại. Ví dụ: truy vấn Hoogle cho số (a -> b) -> [a] -> [b] kéo lên map. Ở đây, bạn có chức năng loại String -> String -> IO(); bạn muốn có chức năng loại (String -> String -> IO()) -> [String] -> [String] -> IO(). Hoogle thường có thể tự tổng quát, nhưng nó gặp sự cố ở đây, vì vậy hãy giúp nó ra: Bạn chỉ muốn (a -> a -> IO()) -> [a] -> [a] -> IO() cho bất kỳ a nào. Nếu bạn Hoogle for that type signature, kết quả đầu tiên là zipWithM_ :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m() trong mô-đun Control.Monad, thực hiện chính xác những gì bạn muốn. Đây là một phần của một gia đình của các chức năng, với mức độ khác nhau tính tổng quát:

Vì vậy, trong trường hợp sử dụng cụ thể của bạn, chúng tôi sẽ phải-có một vài thay đổi-the thêm sau:

import Data.List (isPrefixOf) 

... 

-- I got rid of `head` because it's a partial function, and I prefer `map` to 
-- list comprehensions for simple things  
do files <- getDirectoryContents "tests/libraries" 
    let names = filter (not . ("." `isPrefixOf`)) files 
     libs = map ("tests/libraries/" ++) names 
    zipWithM_ runParseTest names libs 
1

Trong khi chờ đợi câu trả lời tôi đã tạo ra một giải pháp của riêng tôi với một chức năng mới được gọi là map2M_ dựa trên mã nguồn cho mapmapM_:

map2 :: (a -> b -> c) -> [a] -> [b] -> [c] 
map2 _ [] _   = [] 
map2 _ _ []   = [] 
map2 f (a:as) (b:bs) = f a b : map2 f as bs 

map2M_ :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m() 
map2M_ f as bs = sequence_ (map2 f as bs) 
Các vấn đề liên quan