我不是Haskell程序员,但我对以下问题感到好奇。
非正式功能规范:
让MapProduct成为一个函数,它接受一个名为F的函数和多个列表。它返回一个列表,其中包含调用F的结果,每个可能的组合中每个列表都有一个参数。
示例:
调用MapProduct,其中F是一个只返回其参数列表和两个列表的函数。其中一个列表包含整数1和2,另一个包含字符串“a”和“b”。它应该返回一个包含列表的列表:1和“a”,1和“b”,2和“a”,2和“b”。
问题:
答案 0 :(得分:8)
Prelude> :m + Control.Applicative
Prelude Control.Applicative> (,,) <$> [1,2,3] <*> ["a","b","c"] <*> [0.8, 1.2, 4.4]
[(1,"a",0.8),(1,"a",1.2),...,(3,"c",4.4)]
F的类型取决于您要应用的列表。 <$>
此处为fmap
,(<*>) :: f(a->b) -> f a -> f b
此处为f = []
。
- 你能处理不同列表作为输入吗? (例如输入列表中的1和“a”)
没有异构列表这样的东西。但你可以simulate a heterogeneous list for a specific context with existential types。然后你可以使用上面的方法来做MapProduct。
*Main Control.Applicative> :i SB
data ShowBox where
SB :: forall s. (Show s) => s -> ShowBox
-- Defined at v.hs:1:35-36
*Main Control.Applicative> [SB 2, SB "a", SB 6.4]
[2,"a",6.4]
*Main Control.Applicative> (,) <$> [SB 2, SB "a", SB 6.4] <*> [SB 'z', SB 44]
[(2,'z'),(2,44),("a",'z'),("a",44),(6.4,'z'),(6.4,44)]
答案 1 :(得分:2)
可以定义适用于任何功能的函数mapProduct
:
{-# LANGUAGE FlexibleInstances, TypeFamilies #-}
module MapProduct (
mapProduct
) where
import Control.Monad
newtype ProdFuncList a b = ProdFuncList [ a -> b ]
class MapProdResult p where
type MapProdArg p
apNext :: ProdFuncList x (MapProdArg p) -> [x] -> p
instance (MapProdResult b) => MapProdResult ([a] -> b) where
type MapProdArg ([a] -> b) = (a -> MapProdArg b)
apNext (ProdFuncList fs) = apNext . ProdFuncList . ap fs
instance MapProdResult [b] where
type MapProdArg [b] = b
apNext (ProdFuncList fs) = ap fs
mapProduct :: (MapProdResult q) => (a -> MapProdArg q) -> [a] -> q
mapProduct f = apNext (ProdFuncList [f])
这是在行动:
> :l MapProduct.hs
[1 of 1] Compiling MapProduct ( MapProduct.hs, interpreted )
Ok, modules loaded: MapProduct.
> mapProduct (+10) [1..4] :: [Int]
[11,12,13,14]
> mapProduct (*) [1..4] [10..12] :: [Int]
[10,11,12,20,22,24,30,33,36,40,44,48]
> mapProduct (\a b c -> a:b:c:[]) "bcs" "ao" "dnt" :: [String]
["bad","ban","bat","bod","bon","bot","cad","can","cat","cod","con","cot","sad","san","sat","sod","son","sot"]
这种方法的缺点是你很可能必须输入注释结果(如上面的例子所示)。简单地直接使用fmap
和ap
会更加惯用:
> :m + Control.Monad
> (+10) `fmap` [1..4]
[11,12,13,14]
> (*) `fmap` [1..4] `ap` [10..12]
[10,11,12,20,22,24,30,33,36,40,44,48]
> (\a b c -> a:b:c:[]) `fmap` "bcs" `ap` "ao" `ap` "dnt"
["bad","ban","bat","bod","bon","bot","cad","can","cat","cod","con","cot","sad","san","sat","sod","son","sot"]
这不需要类型注释,并且完全适用于所有monad,而不仅仅是[]
。
(上面的MapProduct模块也可以轻松地在所有monad上进行推广。我没有这样做,以便明确解决原始问题。)
答案 2 :(得分:1)
您描述的函数与zipWithN函数密切相关。它将具有相同的类型 - 它只会导致更大的结果列表。现在问题是没有办法表达“一个函数,它接受类型t_1, ..., t_n
的N个参数”或“类型[t_1],...,[t_n]
的n个列表”(或“类型的n元组” {1}})在haskell的类型系统中(没有像模板haskell这样的扩展)。这就是为什么没有一个zipWith函数,但是对于每个支持的参数列表都有一个。
所以回答你的问题:
通过为要支持的每个数字N定义一个函数mapProductN来实现它。对于N = 2,它看起来像这样:
([t_1], ..., [t_n]")
或者作为一般蓝图(即伪代码)如何定义任何N的函数:
mapProduct f l1 l2 = [f x1 x2 | x1 <- l1, x2 <- x2]
正如我所说,它与zipWith函数的类型相同,即:
mapProduct f l1 ... ln = [f x1 ... xn | x1 <- l1, ..., xn <- ln]
由于f是函数的第一个参数,因此第一个参数的类型是f的类型(因此对于n = 2,它是zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
zipWith4 :: (a -> b -> c -> d -> e) -> [a] -> [b] -> [c] -> [d] -> [e]
)
好吧,因为它与zipWith具有相同的类型,而zipWith执行其他操作,那就是no。
Haskell不允许没有扩展名的不同列表。
除非您愿意花时间编写无限版本的mapProduct,否则列表数量会有上限。