有没有一种不那么笨拙的方法来执行无限序列的自动编号?
Is there a less clumsy way to perform automatic numbering with infinite sequences?
虽然我的代码可以满足我的需要,但我觉得我缺少一些编码技术来以更简洁的方式实现此类内容。
目标是组合项目并在整个过程中为它们指定一个 Id 值。
这里的代码我觉得可以在很多方面进行简化和改进。如果我知道,如何...
type Foo = | A of int | B of int | C of int
let ids startvalue = Seq.initInfinite (fun i -> i + startvalue)
let makeA ids =
A(Seq.head ids), Seq.skip 1 ids
let makeB ids =
B(Seq.head ids), Seq.skip 1 ids
let makeC ids =
C(Seq.head ids), Seq.skip 1 ids
let config = [makeA; makeA; makeC; makeB]
let create (ids : seq<int>) (cfg : (seq<int> -> Foo * seq<int>) list) : Foo list * seq<int> =
let rec cre ids1 acc cl =
match cl with
| [] -> (acc,ids1)
| x::xs ->
let v,ids2 = x ids1
cre ids2 (acc @ [v]) xs
cre ids [] cfg
let result : Foo list * seq<int> = create (ids 0) config
结果非常简单:
val result : Foo list * seq = ([A 0; A 1; C 2; B 3], )
不知怎的,我觉得应该有一种更简单的方法来完成同样的事情。
事实上,我知道一种使它更简单的方法,但这将涉及可变状态和记忆(因此可能被认为更糟):
let idgen startvalue =
let v = ref startvalue
fun () ->
let result = !v
v := !v + 1
result
有了生成器函数,我至少可以摆脱所有那些元组,我也可以摆脱 create
函数并简单地写:
let ids = idgen 0
let result =
[
A(ids())
A(ids())
C(ids())
B(ids())
]
但也应该存在一种 "functional" 方法来更简单地完成它。
看来你想要的是采用两个序列,一个是函数,另一个是参数,并通过将函数应用于相应的参数来生成新序列,在你的特定情况下,参数是连续的整数,函数是联合案例构造函数。这是一个正确的评估吗?
如果是这样,我会这样做:
let create args funs =
Seq.zip args funs
|> Seq.map (fun (arg, fn) -> fn arg)
|> List.ofSeq
let result = create (ids 0) [A; A; C; B]
type Foo = | A of int | B of int | C of int
let ids startvalue = Seq.initInfinite (fun i -> i + startvalue)
let config = [A; A; C; B]
let create ids i cfg =
let ids' = ids i
let nextI = i + List.length cfg
(Seq.map2 id cfg ids'), nextI
let result, nextI = create ids 0 config
let result2, nextI2 = create ids nextI config
这里有几个注意事项:
A
、B
和 C
是 int -> Foo
类型的构造函数。您可以直接将它们作为函数使用,并消除重复的包装函数;
Seq.map2
能够处理不同长度的序列,忽略较长序列的剩余元素;
id
是fun f number -> f number
的简写,如果觉得id
不清楚,请使用较长的
- 您也可以将
ids
函数重构为 Seq.initInfinite id |> Seq.skip startvalue
。
- 最后,如果您确实需要,可以将序列转换为
List
。
已更新 以接收保留 i
和 nextI
.
正如 Fyodor 在他的回答中指出的那样,您实际上只想将构造函数应用于连续的整数。您可以为此使用内置的 mapi,将整个程序编写为:
type Foo = | A of int | B of int | C of int
let config = [A; A; B; C]
let create offset = List.mapi (fun i f -> f (offset + i))
create 78 config
// val it : Foo list = [A 78; A 79; B 80; C 81]
虽然我的代码可以满足我的需要,但我觉得我缺少一些编码技术来以更简洁的方式实现此类内容。
目标是组合项目并在整个过程中为它们指定一个 Id 值。 这里的代码我觉得可以在很多方面进行简化和改进。如果我知道,如何...
type Foo = | A of int | B of int | C of int
let ids startvalue = Seq.initInfinite (fun i -> i + startvalue)
let makeA ids =
A(Seq.head ids), Seq.skip 1 ids
let makeB ids =
B(Seq.head ids), Seq.skip 1 ids
let makeC ids =
C(Seq.head ids), Seq.skip 1 ids
let config = [makeA; makeA; makeC; makeB]
let create (ids : seq<int>) (cfg : (seq<int> -> Foo * seq<int>) list) : Foo list * seq<int> =
let rec cre ids1 acc cl =
match cl with
| [] -> (acc,ids1)
| x::xs ->
let v,ids2 = x ids1
cre ids2 (acc @ [v]) xs
cre ids [] cfg
let result : Foo list * seq<int> = create (ids 0) config
结果非常简单:
val result : Foo list * seq = ([A 0; A 1; C 2; B 3], )
不知怎的,我觉得应该有一种更简单的方法来完成同样的事情。
事实上,我知道一种使它更简单的方法,但这将涉及可变状态和记忆(因此可能被认为更糟):
let idgen startvalue =
let v = ref startvalue
fun () ->
let result = !v
v := !v + 1
result
有了生成器函数,我至少可以摆脱所有那些元组,我也可以摆脱 create
函数并简单地写:
let ids = idgen 0
let result =
[
A(ids())
A(ids())
C(ids())
B(ids())
]
但也应该存在一种 "functional" 方法来更简单地完成它。
看来你想要的是采用两个序列,一个是函数,另一个是参数,并通过将函数应用于相应的参数来生成新序列,在你的特定情况下,参数是连续的整数,函数是联合案例构造函数。这是一个正确的评估吗?
如果是这样,我会这样做:
let create args funs =
Seq.zip args funs
|> Seq.map (fun (arg, fn) -> fn arg)
|> List.ofSeq
let result = create (ids 0) [A; A; C; B]
type Foo = | A of int | B of int | C of int
let ids startvalue = Seq.initInfinite (fun i -> i + startvalue)
let config = [A; A; C; B]
let create ids i cfg =
let ids' = ids i
let nextI = i + List.length cfg
(Seq.map2 id cfg ids'), nextI
let result, nextI = create ids 0 config
let result2, nextI2 = create ids nextI config
这里有几个注意事项:
A
、B
和C
是int -> Foo
类型的构造函数。您可以直接将它们作为函数使用,并消除重复的包装函数;Seq.map2
能够处理不同长度的序列,忽略较长序列的剩余元素;id
是fun f number -> f number
的简写,如果觉得id
不清楚,请使用较长的- 您也可以将
ids
函数重构为Seq.initInfinite id |> Seq.skip startvalue
。 - 最后,如果您确实需要,可以将序列转换为
List
。
已更新 以接收保留 i
和 nextI
.
正如 Fyodor 在他的回答中指出的那样,您实际上只想将构造函数应用于连续的整数。您可以为此使用内置的 mapi,将整个程序编写为:
type Foo = | A of int | B of int | C of int
let config = [A; A; B; C]
let create offset = List.mapi (fun i f -> f (offset + i))
create 78 config
// val it : Foo list = [A 78; A 79; B 80; C 81]