可变列表追加器函数,用于从 Haskell 中的列表中创建列表列表

Mit*_*ops 2 haskell

我正在查看这个问题,了解如何获取多个列表并将它们转换为列表列表。我有以下几点:

Prelude> x1 = [1,2,3]
Prelude> x2 = [4,5,6]
Prelude> x3 = [7,8,9]
Run Code Online (Sandbox Code Playgroud)

我想看到一些 \function 这可能是可变参数:

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

或者没有映射,其他一些可变参数函数 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]]
Run Code Online (Sandbox Code Playgroud)

我对答案的期望是类似

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)
Run Code Online (Sandbox Code Playgroud)

或者

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)
Run Code Online (Sandbox Code Playgroud)

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

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

AJF*_*mar 5

Haskell 中的多变量函数很难实现。这是因为一个函数基本上只能有一个参数,因此只有通过柯里化才能包含更多参数,这将参数的数量烘焙到函数的类型中。

然而,这并不意味着这是不可能的,尽管有时这需要使用扩展。在这里,我将按照复杂性递增的顺序进行一些介绍。这可能不会很有用,但可能会有所帮助。

有点切题的是,几年前我制作了一个多变量函数示例的存储库,您可能会发现这些示例很有趣,但它们完全相同且质量可疑;即使现在我也不是专业人士,那几年前的事了。


方法 1:使用单独的函数(无扩展)

一个简单但粗略的方法就是定义多个函数来制作一个包含n 个元素的列表,例如:

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

-- Use:
myList = makeList5 1 2 3 4 5
Run Code Online (Sandbox Code Playgroud)

这不是那么美妙。我们能做得更好吗?


方法 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 = []
Run Code Online (Sandbox Code Playgroud)

我会让你去了解这个。


方法 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]
Run Code Online (Sandbox Code Playgroud)

不久前,我对这段代码做了一个更高效的版本


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

我认为这在理论上是最不有趣的解决方案,所以我不会进入坦率乏味的例子。

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


现在可能有更多的方法可以做到这一点,但我希望这些例子对您有所帮助,或者至少具有启发性。

  • 哇!我从不想在我接触的代码库中看到这一点,但最终看到一个“有意义”的基金用例真是太酷了! (2认同)