我阅读了以下类型定义。它有什么作用?
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?
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)
作为一般说明,我会重命名Cons为Next或其他名称。为了避免混淆,创建辅助函数来创建Nill和Next值。
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)