可变列表附加器功能,可从haskell中的列表中创建列表

时间:2018-12-29 19:26:20

标签: haskell

我正在寻找this question,以了解如何获取多个列表并将其转换为列表列表。我有以下内容:

Prelude> x1 = [1,2,3]
Prelude> x2 = [4,5,6]
Prelude> x3 = [7,8,9]

我想看一些\ variable的函数:

Prelude> xs = map (\function -> ???) x1 x2 x3
Prelude> show xs -- that produces this
[[1,2,3], [4,5,6], [7,8,9]]

或者没有地图,还有其他可变参数函数F,例如:

Prelude> xs = F x1 x2 x3 ... x1000
Prelude> show xs -- that produces this
[[1,2,3], [4,5,6], [7,8,9], ...., [1000000,1000001,1000002]]

我对答案的期望是

Prelude> map (:) x1 x2 x3 []

<interactive>:26:1: error:
    • Couldn't match expected type ‘[Integer]
                                    -> [Integer] -> [a0] -> t’
                  with actual type ‘[[Integer] -> [Integer]]’
    • The function ‘map’ is applied to five arguments,
      but its type ‘(Integer -> [Integer] -> [Integer])
                    -> [Integer] -> [[Integer] -> [Integer]]’
      has only two
      In the expression: map (:) x1 x2 x3 []
      In an equation for ‘it’: it = map (:) x1 x2 x3 []
    • Relevant bindings include it :: t (bound at <interactive>:26:1)

Prelude> map (:) $ x1 x2 x3 []

<interactive>:27:11: error:
    • Couldn't match expected type ‘[Integer]
                                    -> [Integer] -> [a0] -> [a]’
                  with actual type ‘[Integer]’
    • The function ‘x1’ is applied to three arguments,
      but its type ‘[Integer]’ has none
      In the second argument of ‘($)’, namely ‘x1 x2 x3 []’
      In the expression: map (:) $ x1 x2 x3 []
    • Relevant bindings include
        it :: [[a] -> [a]] (bound at <interactive>:27:1)

我也没有在Hoogle中找到这种功能,但是可能错误指定了类型签名:

https://www.haskell.org/hoogle/?hoogle=%5Ba%5D+-%3E+%5Ba%5D+-%3E+%5B%5Ba%5D%2C%5Ba%5D%5D

2 个答案:

答案 0 :(得分:4)

Haskell中的多元函数很难实现。这是因为一个函数从根本上只能有一个参数,因此只能通过currying来包含更多参数,这会将参数的数量烘焙到该函数的类型中。

但是,这并不意味着不可能,尽管有时候这需要使用扩展名。在这里,我将按复杂度递增的顺序进行一些操作。这可能不会很有用,但可能会有所帮助。

几年前我切切地做了a respository of examples of polyvariadic functions,您可能会发现它很有趣,但是它们的质量和质量都差不多。我什至现在都不专业,那是几年前的。


方法1:使用单独的功能(无扩展名)

一种简单但粗略的方法是简单地定义多个函数以创建包含 n 个元素的列表,例如:

makeList1 :: a -> [a]
makeList2 :: a -> a -> [a]
-- etc.

-- Use:
myList = makeList5 1 2 3 4 5

这不是那么好。我们可以做得更好吗?


方法2:类型类(需要FlexibleInstances

这更有趣。在这里,我们牺牲了特异性以创建真正的多元函数:

{-# LANGUAGE FlexibleInstances #-}

class MkIntList r where
  mkIntList' :: [Int] -> r

-- No arguments
instance MkIntList [Int] where
  mkIntList' = id

-- One argument, then some others
instance (MkIntList r) => MkIntList (Int -> r) where
  mkIntList' xs x = mkIntList' (xs ++ [x]) -- (Inefficient, but this is an illustration)

-- The variadic function
mkIntList :: (MkIntList r) => r
mkIntList = mkIntList []

-- Use:
myList1 = mkIntList 1 2 3 :: [Int] -- myList1 = [1,2,3]
myList2 = mkIntList :: [Int]       -- myList2 = []

我会离开你的脑袋。


方法3:功能依赖性(需要FlexibleInstancesFunctionalDependencies

这是上一个版本的多态版本,在该版本中,我们必须通过功能依赖项来跟踪类型。

{-# LANGUAGE FlexibleInstances      #-}
{-# LANGUAGE FunctionalDependencies #-}

class MkList a r | r -> a where
  mkList' :: [a] -> r

instance MkList a [a] where
  mkList' = id

instance (MkList a r) => MkList a (a -> r) where
  mkList' xs x = mkList' (xs ++ [x]) -- (Again inefficient)

mkList :: (MkList a r) => r
mkList = retList []

-- Use:
myList1 = mkList 'H' 'i' '!' :: String -- myList1 = "Hi!"
myList2 = mkList True False :: [Bool]  -- myList2 = [True, False]

我不久前做了一个more efficient version of this代码。


方法4:元编程(需要模板Haskell)

我认为这是解决方案中理论上最不有趣的,所以我将不涉及坦率乏味的示例。

此方法涉及创建一个函数,该函数依次通过Template Haskell生成Haskell代码,然后可以在编译时根据此列表的长度使用该函数生成必要的函数。从本质上讲,这是方法1的劳动强度较小(但在编译时较慢)的版本。


如今,可能有更多的方法可以执行此操作,但是我希望您对这些示例有所帮助,或者至少可以启发人。

答案 1 :(得分:2)

主要是,您的方法不起作用的原因是(我认为)您对map的理解有些误解。让我们看一下类型签名:

map :: (a -> b) -> [a] -> [b]

您可以在此处看到map的主要限制是仅将一个列表作为参数传递-因此您不能传递多个列表,这是您尝试做的。另一个无效的原因是map专门用于将函数应用到列表中 内的元素,而您正尝试在之间使用它>多个列表,而无需更改单个元素。

那么如何定义函数?这里的问题是Haskell并不真正支持可变参数功能(但请参见下文)。在Haskell中,如果要支持任意数量的相同类型的参数,则可以将它们连接到一个列表中。也就是fn [a, b, c]而不是fn a b c。因此,让我们在这里尝试:您的函数将是:

fn :: [[a]] -> [[a]]
fn = ???

那么我们如何实现呢?我们想要的是一个包含多个列表的函数,并且为我们提供了一个包含多个列表(参数)的列表,因此...输出与输入完全相同!在这一点上,我们最好忽略fn-或实际上任何尝试的map (:)组合-自己编写列表。因此,您的示例将是:

xs = [x1, x2, x3]

如果即使这对您都不起作用,并且您确实希望使用可变参数功能,那么我建议您回顾一下程序并检查其是否使用了最佳/最简便的方法-请记住XY problem

(附带说明:如果您真的需要它,而没有其他方法来解决您的问题,那么实际上可以在Haskell中定义可变函数-搜索Haskell variadic function,以获取更多信息。但是,这种方法在进行字符串格式设置或高级类型级别的东西时最有用,因此不太可能需要这种方法。