【问题标题】:Avoiding using Option.Value避免使用 Option.Value
【发布时间】:2014-10-17 06:56:32
【问题描述】:

我有一个这样的类型:

type TaskRow =
    {
        RowIndex : int
        TaskId : string
        Task : Task option
    }

函数返回这些记录的列表以进一步处理。执行该处理的一些函数仅与 TaskSomeTaskRow 项目相关。我想知道最好的方法是什么。

天真的方法会做

let taskRowsWithTasks = taskRows |> Seq.filter (fun row -> Option.isSome row.Task)

并将其传递给那些函数,简单地假设Task 永远不会是None 并使用Task.Value,如果我不传入那个特殊列表,就会冒NRE 的风险。这正是当前 C# 代码所做的,但对于 F# 来说似乎相当单一。我不应该“假设”事情,而是让编译器告诉我什么会起作用。

更多的“功能”将是每次值相关时进行模式匹配,然后对choose 不执行/返回任何内容(并使用None 等),但这似乎重复且浪费,因为相同的工作会进行多次。

另一个想法是引入第二种略有不同的类型:

type TaskRowWithTask =
    {
        RowIndex : int
        TaskId : string
        Task : Task
    }

然后将原始列表过滤成这种类型的“子列表”,以便在适当的地方使用。我想从功能的角度来看这没问题,但我想知道是否有更好的、惯用的方式而不求助于这种“帮助类型”。

感谢您的指点!

【问题讨论】:

    标签: f# idioms


    【解决方案1】:

    知道任务已经被过滤是很有价值的,所以有两种不同的类型会很有帮助。除了定义两种不同的类型(虽然在 F# 中这没什么大不了的),您还可以考虑定义一个通用的 Row 类型:

    type Row<'a> = {
        RowIndex : int
        TaskId : string
        Item : 'a }
    

    这使您可以像这样定义投影:

    let project = function
        | { RowIndex = ridx; TaskId = tid; Item = Some t } ->
            Some { RowIndex = ridx; TaskId = tid; Item = t }
        | _ -> None
    
    let taskRowsWithTasks =
        taskRows
        |> Seq.map project
        |> Seq.choose id
    

    如果初始taskRows 值的类型为seq&lt;Row&lt;Task option&gt;&gt;,则生成的taskRowsWithTasks 序列的类型为seq&lt;Row&lt;Task&gt;&gt;

    【讨论】:

    • 为什么不用Seq.choose project 而不是map + choose id
    • 是的,确实,为什么不呢?因为我没有想到,这就是为什么,但显然,它更简单:)
    【解决方案2】:

    我同意你的观点,更“纯功能”的方式是重复模式匹配,我的意思是使用带有 Seq.choose 的函数进行过滤,而不是将其保存到另一个结构中。

    let tasks = Seq.choose (fun {Task = t} -> t) taskRows
    

    问题在于性能,因为它会被计算多次,但您可以使用Seq.cache,因此在幕后将其保存到中间结构中,同时让您的代码看起来更“纯功能”。

    【讨论】:

      猜你喜欢
      • 2012-04-18
      • 1970-01-01
      • 2022-11-15
      • 2023-01-15
      • 2019-01-15
      • 2017-11-07
      • 2017-09-12
      • 1970-01-01
      • 2015-08-21
      相关资源
      最近更新 更多