简化Haskell中的嵌套列表输入

时间:2020-03-08 12:23:37

标签: haskell

我正在制作一个函数来展平任意深度的嵌套列表。

flatten.hs

data NestedList a = Regular a | Nested [NestedList a]

flatten::NestedList a -> [a]
flatten (Regular a) = [a]
flatten (Nested a) = concatMap flatten a

现在,如果我想拼合列表[1,2,3,[1,[1, 2]],2],则必须像(Nested [Regular 1, Regular 2, Regular 3, Nested [Regular 1, Nested [Regular 1, Regular 2]], Regular 2])一样输入。有没有一种方法可以简化输入值?我了解OverloadedLists,但不知道如何使用它们。

1 个答案:

答案 0 :(得分:3)

您可以通过OverloadedLists到达那里:

此处的关键是定义一个IsList实例。对于超载列表,如果GHC看到类似以下内容:

[x,y,z]

并可以将xyz解析为所有相同的类型item,然后将它们放入列表[x,y,z] :: [item]中并调用:

fromList :: [item] -> NestedList a

您必须选择正确的item定义才能使它起作用。并且,由于您希望能够编写:

[[another_nested_list],[another_nested_list]]

内部列表的解析方式完全相同,事实证明您也需要item成为NestedList a

fromList :: [NestedList a] -> NestedList a

这给出了IsList实例:

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedLists #-}

import GHC.Exts

data NestedList a = Regular a | Nested [NestedList a] deriving (Show)

instance IsList (NestedList a) where
  type Item (NestedList a) = NestedList a
  fromList = Nested

在此位置,您可以编写:

> flatten [Regular 1, Regular 2, Regular 3, [Regular 1, [Regular 1, Regular 2]], Regular 2]
[1,2,3,1,1,2,2]

不幸的是,如果没有Regular,它就无法工作,因为1不能被解析为item,即NestedList a

您可以通过定义Num实例来将整数文字解析为NestedList a来耍个肮脏的招,就像这样:

instance (Num a) => Num (NestedList a) where
  fromInteger = Regular . fromInteger

那会让你写:

flatten [1,2,3,[1,[1,2]],2]
> [1,2,3,1,1,2,2]

这使它仅适用于整数。如果您尝试写:

> flatten [[1.5,2.5]]
> flatten [["bar"],[["foo"]]

您会得到错误。您需要定义一个Fractional实例来处理1.5和一个IsString实例(使用OverloadedStrings)来处理"bar"