F#:将 seq<'A> 转换为 seq<'B> 的最快方法
F#: Fastest way to convert a seq<'A> to seq<'B>
我将 Marten 用作事件存储,特别是用于获取事件流。
type AccountCreation = {
Owner: string
AccountId: Guid
CreatedAt: DateTimeOffset
StartingBalance: decimal
}
type AccountEvents =
| AccountCreated of AccountCreation
| AccountCredited of Transaction
| AccountDebited of Transaction
let settings = {
Host = "localhost"
DatabaseName = "postgres"
UserName = "root"
Password = "root"
EventTypes = eventTypes
}
use store = createDocumentStore settings
use session = store.LightweightSession()
let khalidId = Guid.NewGuid()
let billId = Guid.NewGuid()
let khalid = AccountEvents.AccountCreated({
Owner = "Khalid Abuhakmeh"
AccountId = khalidId
StartingBalance = 1000m
CreatedAt = DateTimeOffset.UtcNow
})
let bill = {
Owner = "Bill Boga"
AccountId = billId
StartingBalance = 0m
CreatedAt = DateTimeOffset.UtcNow
}
session.Events.Append(khalidId, khalid) |> ignore
session.Events.Append(billId, bill) |> ignore
session.SaveChanges()
let stream = session.Events.FetchStream()
stream
被 IReadOnlyList<IEvent>
和 IEvent
定义为:
public interface IEvent
{
Guid Id { get; set; }
int Version { get; set; }
long Sequence { get; set; }
object Data { get; }
Guid StreamId { get; set; }
string StreamKey { get; set; }
DateTimeOffset Timestamp { get; set; }
string TenantId { get; set; }
void Apply<TAggregate>(TAggregate state, IAggregator<TAggregate> aggregator) where TAggregate : class, new();
}
我想将每个 IEvent
转换为 AccountEvents
,如果 Data
属性 的基础类型是 AccountEvents
(如果不是项目在结果序列中没有产生。
在 C# 中,我会简单地使用关键字 as
来实现它,但在 F# 中,我不确定最快的 F#-ish 方式(就性能而言)是什么。
我最终得到了以下代码:
let seqCastOption<'T> sequence =
sequence
|> Seq.map(fun x ->
match box x with
| :? 'T as value -> Some value
| _ -> None)
let fetchStream<'T> (session: IDocumentSession) (id: Guid) =
let stream = session.Events.FetchStream(id)
stream
|> Seq.map(fun x -> x.Data)
|> seqCastOption<'T>
|> Seq.filter (fun x -> x.IsSome)
|> Seq.map(fun x -> x.Value)
但这看起来相当 "expensive",我想知道将 .Data
转换为 Option<AccountEvents>
+ 过滤 IsSome
的步骤是否可以全部完成立刻。
Seq.choose
是您一直在寻找的功能。你给它一个函数,它接受一个 'A
和 returns 一个 'B option
,它产生 Some
的 'B
值。对于您的使用场景,它看起来像这样:
let castOption<'T> x =
match box x with
| :? 'T as value -> Some value
| _ -> None
let fetchStream<'T> (session: IDocumentSession) (id: Guid) =
let stream = session.Events.FetchStream(id)
stream
|> Seq.map(fun x -> x.Data)
|> Seq.choose castOption<'T>
rmunn 的回答中提到的 Seq.choose
函数对于了解这种情况非常有用,但对于这种确切情况,我建议使用内置的 .NET 方法 Enumerable.OfType<'T>
,它确实正是您想要的,并且可能已经过优化:
open System.Linq
let fetchStream<'T> (session: IDocumentSession) (id: Guid) =
let stream = session.Events.FetchStream(id)
stream
|> Seq.map(fun x -> x.Data)
|> Enumerable.OfType<'T>
我将 Marten 用作事件存储,特别是用于获取事件流。
type AccountCreation = {
Owner: string
AccountId: Guid
CreatedAt: DateTimeOffset
StartingBalance: decimal
}
type AccountEvents =
| AccountCreated of AccountCreation
| AccountCredited of Transaction
| AccountDebited of Transaction
let settings = {
Host = "localhost"
DatabaseName = "postgres"
UserName = "root"
Password = "root"
EventTypes = eventTypes
}
use store = createDocumentStore settings
use session = store.LightweightSession()
let khalidId = Guid.NewGuid()
let billId = Guid.NewGuid()
let khalid = AccountEvents.AccountCreated({
Owner = "Khalid Abuhakmeh"
AccountId = khalidId
StartingBalance = 1000m
CreatedAt = DateTimeOffset.UtcNow
})
let bill = {
Owner = "Bill Boga"
AccountId = billId
StartingBalance = 0m
CreatedAt = DateTimeOffset.UtcNow
}
session.Events.Append(khalidId, khalid) |> ignore
session.Events.Append(billId, bill) |> ignore
session.SaveChanges()
let stream = session.Events.FetchStream()
stream
被 IReadOnlyList<IEvent>
和 IEvent
定义为:
public interface IEvent
{
Guid Id { get; set; }
int Version { get; set; }
long Sequence { get; set; }
object Data { get; }
Guid StreamId { get; set; }
string StreamKey { get; set; }
DateTimeOffset Timestamp { get; set; }
string TenantId { get; set; }
void Apply<TAggregate>(TAggregate state, IAggregator<TAggregate> aggregator) where TAggregate : class, new();
}
我想将每个 IEvent
转换为 AccountEvents
,如果 Data
属性 的基础类型是 AccountEvents
(如果不是项目在结果序列中没有产生。
在 C# 中,我会简单地使用关键字 as
来实现它,但在 F# 中,我不确定最快的 F#-ish 方式(就性能而言)是什么。
我最终得到了以下代码:
let seqCastOption<'T> sequence =
sequence
|> Seq.map(fun x ->
match box x with
| :? 'T as value -> Some value
| _ -> None)
let fetchStream<'T> (session: IDocumentSession) (id: Guid) =
let stream = session.Events.FetchStream(id)
stream
|> Seq.map(fun x -> x.Data)
|> seqCastOption<'T>
|> Seq.filter (fun x -> x.IsSome)
|> Seq.map(fun x -> x.Value)
但这看起来相当 "expensive",我想知道将 .Data
转换为 Option<AccountEvents>
+ 过滤 IsSome
的步骤是否可以全部完成立刻。
Seq.choose
是您一直在寻找的功能。你给它一个函数,它接受一个 'A
和 returns 一个 'B option
,它产生 Some
的 'B
值。对于您的使用场景,它看起来像这样:
let castOption<'T> x =
match box x with
| :? 'T as value -> Some value
| _ -> None
let fetchStream<'T> (session: IDocumentSession) (id: Guid) =
let stream = session.Events.FetchStream(id)
stream
|> Seq.map(fun x -> x.Data)
|> Seq.choose castOption<'T>
rmunn 的回答中提到的 Seq.choose
函数对于了解这种情况非常有用,但对于这种确切情况,我建议使用内置的 .NET 方法 Enumerable.OfType<'T>
,它确实正是您想要的,并且可能已经过优化:
open System.Linq
let fetchStream<'T> (session: IDocumentSession) (id: Guid) =
let stream = session.Events.FetchStream(id)
stream
|> Seq.map(fun x -> x.Data)
|> Enumerable.OfType<'T>