如何对 Elm 中的列表元素进行编号?
How to number list elements in Elm?
给定以下列表
List Records
其中 Record
是
type Record
= RecordA A
| RecordB B
和A
和B
是具有pos
字段的类型别名:
type alias A =
{ pos : Maybe Int
, type : Char
}
我如何创建一个新列表来为后续 A
和 B
记录编号?我想在第一条 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)
如果我没听错,你 有 一个包含 A
和 B
类型的列表?然后,您可能希望对映射到列表((\n -> RecordA (A(Just n) 'a'))
部分)的函数中的类型进行模式匹配,也许构造两个列表并将它们 zip
在一起。但正如@glennsl 已经说过的那样,设计留下了一些悬而未决的问题。
给定以下列表
List Records
其中 Record
是
type Record
= RecordA A
| RecordB B
和A
和B
是具有pos
字段的类型别名:
type alias A =
{ pos : Maybe Int
, type : Char
}
我如何创建一个新列表来为后续 A
和 B
记录编号?我想在第一条 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)
如果我没听错,你 有 一个包含 A
和 B
类型的列表?然后,您可能希望对映射到列表((\n -> RecordA (A(Just n) 'a'))
部分)的函数中的类型进行模式匹配,也许构造两个列表并将它们 zip
在一起。但正如@glennsl 已经说过的那样,设计留下了一些悬而未决的问题。