计算递归类型别名列表中的元素

时间:2017-11-06 21:36:41

标签: recursion elm

使用elm-compiler存储库中的递归类型别名提示,一个解决方案产生一个类型签名:

type alias Comment =
  { message : String
  , upvotes : Int
  , downvotes : Int
  , responses : Maybe Responses
  }

type Responses = Responses (List Comment)

(其中responses已扩展为在此处键入Maybe Responses以允许空响应列表。)

我有兴趣计算这样一个列表中的元素数量,虽然我目前的解决方案似乎比需要的要复杂得多。

count : List Comment -> Int
count comments =
    let
        responses =
            List.concatMap (\c -> flatList c) comments
    in
    List.length responses


flatList : Comment -> List Comment
flatList root =
    let
        rest =
            case root.children of
                Just responseList ->
                    List.concatMap (\child -> flatList child) <| unwrapResponses responseList
                Nothing ->
                    []
    in
    root :: rest

unwrapResponses : Responses -> List Comment
unwrapResponses responses =
    case responses of
        Responses comments ->
            comments

实际上,这会打开每个Responses子列表并递归展平它。然后,对于每个父Comments,我们将每个平面Responses列表连接在一起,最后得到此列表的长度。

由于我没有使用这个扁平列表,我更倾向于只计算每个List.length,因为我在列表中重复,然后折叠或求和结果(或者,使用其他方法检索总元素数)。但是,我不确定如何在不返回flatList结果的情况下生成这样的解决方案。

1 个答案:

答案 0 :(得分:2)

听起来你需要一个专门的折叠功能来评论。折叠是一种访问结构中每个元素的想法,它使用一个接受元素和某种累加器值的函数,这样就可以保持一步到位的状态。

旁注:我建议您将评论responses定义为Responses而不是Maybe Responses,因为空列表和Nothing实际上代表相同的事情。

您可以为这样的评论列表定义foldl

foldl : (Comment -> b -> b) -> b -> List Comment -> b
foldl f =
    List.foldl
        (\c acc ->
            case c.responses of
                Responses responses ->
                    foldl f (f c acc) responses
        )

这意味着它将首先使用您的函数访问注释节点,然后从左到右访问所有子节点,并在此过程中累积结果。

要使用它来确定长度,您只需忽略注释并沿途增加一个计数器:

length : List Comment
length =
    foldl (\_ acc -> acc + 1) 0

您会看到definition of List.length使用foldl的相同构思来实现它。