有没有一种不那么笨拙的方法来执行无限序列的自动编号?

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

这里有几个注意事项:

  1. ABCint -> Foo 类型的构造函数。您可以直接将它们作为函数使用,并消除重复的包装函数;
  2. Seq.map2能够处理不同长度的序列,忽略较长序列的剩余元素;
  3. idfun f number -> f number的简写,如果觉得id不清楚,请使用较长的
  4. 您也可以将 ids 函数重构为 Seq.initInfinite id |> Seq.skip startvalue
  5. 最后,如果您确实需要,可以将序列转换为List

已更新 以接收保留 inextI.

正如 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]