F# 类型带有“and”和 Lazy?

ca9*_*3d9 2 f#

我阅读了以下类型定义。它有什么作用?

type StreamCell<'a> =
    | Nill
    | Cons of 'a * Stream<'a>
and Stream<'a> = Lazy<StreamCell<'a>>
Run Code Online (Sandbox Code Playgroud)

我尝试用类型定义值。

let x = Lazy(1::2::Nill)  // Type is Lazy<list<int>>
let y = Lazy(Nill::1)  // Lazy<StreamCell<obj>>
Run Code Online (Sandbox Code Playgroud)

x我认为和的类型y应该是 StreamCell?

Dav*_*aab 5

F# 中的and是为了定义递归类型而存在的。在大多数其他语言中,不存在顺序。一旦定义了类、函数等。您可以访问它。但在 F# 中顺序很重要。您只能访问已定义的想法。

因此,通常不可能定义递归类型,或者一般的循环类型。我认为这是个好主意。但有时,您需要这样,在这种情况下,您必须定义应该使用 递归的类型and

一个简单的例子是

type A = A of B
type B = B of A
Run Code Online (Sandbox Code Playgroud)

这将会失败。因为当你定义的时候A,就没有了B。所以B必须先定义A。但你B之前不能定义,A因为它取决于A.

因此,不要使用,而是type使用and

type A = A of B
 and B = B of A
Run Code Online (Sandbox Code Playgroud)

您无法创建这种类型的值,因为它是无限的,但这只是为了理解问题。接下来,你的例子不是最好的,因为。

and Stream<'a> = Lazy<StreamCell<'a>>
Run Code Online (Sandbox Code Playgroud)

只是一个类型别名。在这里您定义Stream<'a>为 的别名Lazy<StreamCell<'a>>。但编译器通常不会使用Stream<'a>. 仅当您在函数定义中手动编写类型时,这才会有帮助。你的定义也可以是。

type StreamCell<'a> =
    | Nill
    | Cons of 'a * Lazy<StreamCell<'a>>
Run Code Online (Sandbox Code Playgroud)

在你的例子中

let x = Lazy(1::2::Nill) 
Run Code Online (Sandbox Code Playgroud)

您使用::,这不是Cons在流中定义的。您将使用 F# 定义的 cons 运算符,即内置的list. Lazy<List<int>>这就是你将其视为一种类型的原因。

如果您想使用两个值定义流,则需要编写。

let x = Cons(1,lazy Cons(2, lazy Nill))
Run Code Online (Sandbox Code Playgroud)

作为一般说明,我会重命名ConsNext或其他名称。为了避免混淆,创建辅助函数来创建NillNext值。


添加

and还可以用于更改定义的顺序,并使哪些类型属于一起更加明显。

type Person = {
    Name: string
    Sex:  Sex
}

and Sex =
    | Male
    | Female

let person = { Name="David"; Sex=Male }
Run Code Online (Sandbox Code Playgroud)

例子

这是一个完整的示例,我将如何根据您提供的内容执行 Stream 类型。

type Stream<'a> =
    | Nill
    | Next of 'a * Lazy<Stream<'a>>

let nill     = Nill
let next h t = Next(h,t)

let rec unfold gen state =
    match gen state with
    | None          -> Nill
    | Some(x,state) -> next x (lazy unfold gen state)

let rec fold f acc xs =
    match xs with
    | Nill      -> acc
    | Next(h,t) -> fold f (f acc h) (t.Force())

let rec rev stream =
    fold (fun acc x -> next x (lazy acc)) nill stream

let toList stream =
    fold (fun acc x -> x::acc ) [] (rev stream)

let rec take x stream =
    if x > 0 then
        match stream with
        | Nill      -> Nill
        | Next(h,t) -> next h (lazy take (x-1) (t.Force()))
    else
        Nill

let fromTo start stop =
    unfold (fun acc -> if acc<stop then Some(acc,acc+1) else None) start

let x  = next 1   (lazy next 2   (lazy next 3   (lazy nill)))
let y  = next 1.0 (lazy next 2.0 (lazy next 3.0 (lazy nill)))

printfn "%A" (toList (take 2 x))
printfn "%A" (toList (take 2 y))
printfn "%A" (toList (take 2 (fromTo 1 100)))
printfn "%A" (toList (take 5 (fromTo 1 1_000_000_000)))
Run Code Online (Sandbox Code Playgroud)