如何给Elm中的列表元素编号?



给定以下列表

List Records

其中Record

type Record
= RecordA A
| RecordB B

ABpos字段的类型别名:

type alias A =
{ pos : Maybe Int
, type : Char
}

我如何创建一个新的列表,编号随后的AB记录?我想在第一个A记录中有pos = 1,在第二个A记录中有pos = 2,在第一个B记录中有pos = 1,等等(原始列表在pos字段中没有数字)

这是一个允许变异的语言的解决方案。

let countA = 0;
let countB = 0;
for (el of my_array) {
el.pos = (el.type == 'A') ? ++countA : ++countB;
}

您可以将任何使用可变变量的迭代算法转换为不太容易的递归算法,只需将可变变量作为函数参数:

enumerate : List Record -> Int -> Int -> List Record
enumerate list countA countB =
case list of
[] ->
[]
(RecordA el) :: rest ->
RecordA { el | pos = Just countA } :: enumerate rest (countA + 1) countB
(RecordB el) :: rest ->
RecordB { el | pos = Just countB } :: enumerate rest countA (countB + 1)
然而,这不是尾部递归,因此会在较大的列表上溢出堆栈。每次使用它时都必须指定初始计数,这也有点不方便。可以通过使用内部函数并向其添加一个累加器参数来解决这两个问题:
enumerate : List Record -> List Record
enumerate list =
let
aux els acc posA posB =
case list of
[] ->
acc
(RecordA el) :: rest ->
aux rest (RecordA { el | pos = Just posA } :: acc) (posA + 1) posB
(RecordB el) :: rest ->
aux rest (RecordB { el | pos = Just posB } :: acc) posA (posB + 1)
in
aux list [] 0 0

您似乎在这里有一些更深层次的数据建模问题,但它看起来也不像您的实际类型,所以希望您最终也能弄清楚。这至少能让你离目标更近一点。

延迟响应:映射表达式实际上可以做您想做的事情。我没有彻底检查这一点,因为我也懒得让样板文件在ellie中实际编译:

List.map (n -> RecordA (A(Just n) 'a')) (List.range 1 5)

如果我得到你正确,你列表包含AB类型?然后,您可能希望对映射到列表((n -> RecordA (A(Just n) 'a'))部分)的函数中的类型进行模式匹配,可能构造两个列表并将它们一起zip。但正如@glennsl已经说过的,这个设计留下了一些悬而未决的问题。