我有这样的类型:
type TaskRow =
{
RowIndex : int
TaskId : string
Task : Task option
}
函数返回要进一步处理的这些记录的列表。执行该处理的某些功能仅与TaskRow
为Task
的{{1}}项相关。我想知道最好的方法是什么。
天真的方式就是
Some
并将其传递给这些函数,只需假设let taskRowsWithTasks = taskRows |> Seq.filter (fun row -> Option.isSome row.Task)
永远不会Task
并使用None
,如果我没有传入一个特殊列表,则冒险尝试NRE。这正是当前C#代码所做的,但对于F#而言似乎相当单一。我不应该“假设”事情,而是让编译器告诉我什么会起作用。
更多'功能'将是每次值相关时进行模式匹配,然后对Task.Value
执行/返回任何内容(并使用choose
等),但这似乎是重复和浪费的同样的工作会多次完成。
另一个想法是引入第二种略有不同的类型:
None
然后将原始列表过滤到这种类型的“子列表”中,以便在适当的时候使用。我想从功能的角度来看这是可以的,但我想知道是否有一种更好的,惯用的方式而不诉诸这种“助手类型”。
感谢您的任何指示!
答案 0 :(得分:5)
知道任务已经过滤,有很多价值,因此有两种不同类型可能会有所帮助。你可以考虑定义一个通用的Row
类型,而不是定义两种不同的类型(在F#中,这不是那么大的交易):
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<Row<Task option>>
,则生成的taskRowsWithTasks
序列的类型为seq<Row<Task>>
。
答案 1 :(得分:3)
我同意你的观点,更多“纯粹的功能”方式是重复模式匹配,我的意思是使用Seq.choose
进行过滤的函数,而不是将其保存到另一个结构。
let tasks = Seq.choose (fun {Task = t} -> t) taskRows
问题在于性能,因为它会多次计算,但您可以使用Seq.cache
,因此在幕后将其保存到中间结构中,同时保持代码更“纯粹功能”。