Haskell测验:一个简单的功能

时间:2010-03-23 14:43:29

标签: haskell

我不是Haskell程序员,但我对以下问题感到好奇。

非正式功能规范:

让MapProduct成为一个函数,它接受一个名为F的函数和多个列表。它返回一个列表,其中包含调用F的结果,每个可能的组合中每个列表都有一个参数。

示例:

调用MapProduct,其中F是一个只返回其参数列表和两个列表的函数。其中一个列表包含整数1和2,另一个包含字符串“a”和“b”。它应该返回一个包含列表的列表:1和“a”,1和“b”,2和“a”,2和“b”。

问题:

  • MapProduct是如何实现的?
  • 功能的类型是什么?什么是F的类型?
  • 可以通过查看其类型来猜测函数的作用吗?
  • 你能处理不同列表作为输入吗? (例如输入列表中的1和“a”)
  • 您需要引入哪些额外限制(如果有)来实现MapProduct?

3 个答案:

答案 0 :(得分:8)

  • MapProduct是如何实现的?

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的类型?

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"]

这种方法的缺点是你很可能必须输入注释结果(如上面的例子所示)。简单地直接使用fmapap会更加惯用:

> :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,否则列表数量会有上限。