我需要运行一个多次接受两个参数的函数。我有两个包含这些参数的列表,我希望能够使用map
或类似的东西来调用带有相应args的函数。
我要调用的函数有这种类型:
runParseTest :: String -> String -> IO()
列表的创建方式如下:
-- 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]
因此,我要说names
包含["test1.js", "test2.js", "test3.js"]
而libs
包含["tests/libraries/test1.js", "tests/libraries/test2.js", "tests/libraries/test3.js"]
我想这样称呼他们:
runParseTest "test1.js" "tests/libraries/test1.js"
runParseTest "test2.js" "tests/libraries/test2.js"
runParseTest "test3.js" "tests/libraries/test3.js"
我知道我可以创建一个帮助函数,可以很容易地做到这一点,但是出于兴趣,是否可以使用map
在一行中完成?
这是我到目前为止所做的,但显然第一个论点总是“测试”:
mapM_ (runParseTest "test") libs
如果不清楚,我道歉。如有必要,我可以提供更多信息。
答案 0 :(得分:16)
这是使用Hoogle的好时机! Hoogle是搜索Haskell 类型的搜索引擎。例如,(a -> b) -> [a] -> [b]
的Hoogle查询提升map
。在这里,你有一个String -> String -> IO ()
类型的函数;你想要一个(String -> String -> IO ()) -> [String] -> [String] -> IO ()
类型的函数。 Hoogle可以经常自我概括,但它在这里遇到了麻烦,所以让我们帮忙吧:你只需要(a -> a -> IO ()) -> [a] -> [a] -> IO ()
任何a
。如果您Hoogle for that type signature,则zipWithM_ :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m ()
模块中的第一个结果是Control.Monad,它完全符合您的要求。这是一系列功能的一部分,具有不同程度的通用性:
zip :: [a] -> [b] -> [(a,b)]
,它将两个列表配对,截断较短的列表。zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
,它对来自两个列表中每个列表的元素运行提供的函数; zip = zipWith (,)
。zipWithM :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m [c]
,就像monad中的zipWith
一样; zipWithM f xs ys = sequence $ zipWith f xs ys
。zipWithM_ :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m ()
,与zipWithM
类似,但会丢弃其结果; zipWithM_ f xs ys = zipWithM f xs ys >> return () = sequence_ $ zipWith f xs ys
。zip3 :: [a] -> [b] -> [c] -> [(a, b, c)]
,我相信其功能可以解决: - )zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
,与三个列表中的zipWith
类似; zipWith3 = zip (,,)
。zipN
和zipWithN
个函数in Data.List,通过zip7
/ zipWith7
上升。 (可以说,这从id :: [a] -> [a]
开始为zip1
,map :: (a -> b) -> [a] -> [b]
为zipWith1
,这是您的问题所在。)ZipList
应用函子。给定一些列表xs1
... xsN
,然后runZipList $ f <$> ZipList xs1 <*> ZipList xs2 <*> ... <*> ZipList xsN = runZipList $ liftAN f (ZipList xs1) ... (ZipList xsN
)的行为就像zipWithN f xs1 ... xsN
。因此,在您的特定用例中,我们将进行一些额外的更改 - 以下内容:
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 :(得分:11)
让我们说名字包含
["test1.js", "test2.js", "test3.js"]
和libs包含["tests/libraries/test1.js", "tests/libraries/test2.js", "tests/libraries/test3.js"]
我想这样称呼他们:
runParseTest "test1.js" "tests/libraries/test1.js"
runParseTest "test2.js" "tests/libraries/test2.js"
runParseTest "test3.js" "tests/libraries/test3.js"
可以使用zip
:
map (\(a,b) -> runParseTest a b) $ zip names libs
或者uncurry runParseTest
:
map (uncurry runParseTest) $ zip names libs
或zipWith
:
zipWith runParseTest names libs
和Ozgur一样,monad有一些类似的东西:
> :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 ()
答案 2 :(得分:3)
您正在寻找zipWithM_。
你说你可以编写一个帮助函数来执行此操作。这意味着您知道要查找的功能的类型。在这种情况下,您可以使用hoogle。
(尝试:Monad m =&gt; [a] - &gt; [b] - &gt; m())
答案 3 :(得分:1)
在等待答案的同时,我根据map2M_
和map
的源代码创建了一个名为mapM_
的新功能的自己的解决方案:
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)