使用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
结果的情况下生成这样的解决方案。
答案 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
的相同构思来实现它。