使用接口和 linq 语句将 c# 转换为 f#

c# to f# conversion with interfaces and linq statements

我正在尝试将这个使用接口和 Linq 的 c# 方法转换为 f#:

public static IEnumerable<ModelEngines> CurrentEngineBuilds(IEnumerable<CurrentModels> thisYearModels, DateTime startRunDate, DateTime endRunDate)
        {
            var engineOptions =
                currentModelYear.SelectMany(c => c.Engines)
                    .Where(e => e.EngineProdStartDate >= startRunDate & e.EngineProdStopDate <= endRunDate);
            return engineOptions;
        }

在上面的方法中,我返回了 engineOptions 的集合 - 类型是 IEnumerable。我正在过滤该集合,以便该集合仅包含具有当前车型年份的特定生产范围的 engineOptions。

我有下面这个粗略的、非工作的翻译:

let CurrentEngineBuilds : <IEnumerable<ModelEngines>> (thisYearModels : IEnumerable<CurrentModels>) (startRunDate : DateTime) (endRunDate : DateTime) =
    query { 
        for engineOptions in currentModelYear.engineOptions do
        where (engineOptions.EngineProdStartDate >= startRunDate and engineOptions.EngineProdEndDate >= endRunDate )
        select (engineOptions)             
    }
    engineOptions 

这个任务比我预想的要复杂。我已经阅读了一些 f# 教程来了解基础知识,但似乎与接口有关 and/or linq 非常困难。

我遇到的一些问题:

关于如何将 c# 转换为 f# 的任何提示?

IEnumerable<'T> 在 F# 中被命名为 seq<'T>。在这种情况下,我看不出有任何理由使用 LINQ query 表达式;更地道的 F# 翻译将使用 the Seq module:

let CurrentEngineBuilds thisYearModels startRunDate endRunDate =
    thisYearModels
    //     v--- SelectMany
    |> Seq.collect (fun c -> c.Engines)
    //     v--- Where
    |> Seq.filter (fun e ->
        e.EngineProdStartDate >= startRunDate && e.EngineProdStopDate <= endRunDate)

如果thisYearModels的类型不是自动推导出来的,可以在函数签名中注解:

let CurrentEngineBuilds (thisYearModels:seq<CurrentModels>) startRunDate endRunDate = ...

或在正文中:

let CurrentEngineBuilds thisYearModels startRunDate endRunDate =
    (thisYearModels:seq<CurrentModels>)
    |> Seq.collect (fun c -> c.Engines)
    |> Seq.filter (fun e ->
        e.EngineProdStartDate >= startRunDate && e.EngineProdStopDate <= endRunDate)
// or
let CurrentEngineBuilds thisYearModels startRunDate endRunDate =
    thisYearModels
    |> Seq.collect (fun (c:CurrentModels) -> c.Engines)
    |> Seq.filter (fun e ->
        e.EngineProdStartDate >= startRunDate && e.EngineProdStopDate <= endRunDate)

您实际上可以在 F# 中使用 LINQ 而不会出现任何问题。这是您原始样本的几乎直接端口(我假设 currentModelYear 是一种类型,您的意思是 thisYearModels

open System.Linq
let currentEngineBuilds(thisYearModels:seq<CurrentModels>, startRunDate, endRunDate) =
    let engineOptions =
        thisYearModels
            .SelectMany(fun e -> e.Engines)
            .Where(fun e -> e.StartDate >= startRunDate && e.StopDate <= endRunDate)
    engineOptions // return keyword not required

出于此 post 的目的,您可以将 seq<T> 视为 IEnumerable<T> 的 shorthand。

话虽如此,我认为您会发现 Seq 模块更适合 F#,因为它旨在利用 F# 语言功能(例如元组),并且可以协助类型推断过程.

|> 或 "pipe" 运算符是 F# 将函数应用于值的惯用方式。

v |> f 与前面指出的 f v 相同。

另一方面,Monadic 链接完全是另一回事。您在原始 post 的 query 计算表达式中使用了单子链接,它是 惯用的。

希望这有助于在您的理解中区分这两个(相当复杂的)概念! :)