【发布时间】:2014-07-23 00:55:18
【问题描述】:
在函数式语言(使用 F#)中,我正在努力在具有单一职责的函数式组合的优势和获得单次迭代优于序列的性能之间找到平衡。有什么代码模式建议/例子可以实现这两者?
我在计算理论方面没有扎实的背景,我一遍又一遍地遇到这种一般模式:迭代集合并希望在迭代时产生副作用以避免对同一集合或其结果集的进一步迭代.
一个典型的例子是“减少”或“过滤”功能:过滤时有很多次我想根据过滤器的结果采取额外的步骤,但我想避免过滤后的第二次枚举结果。
让我们将输入验证作为一个简单的问题陈述:
- 命名输入数组
- 通过管道传递给“isValid”函数过滤器
- 副作用:记录无效的输入名称
- 管道有效输入以进一步执行
问题示例
在 F# 中,我最初可能会这样写:
inputs
// how to log invalid or other side-effects without messing up isValid??
|> Seq.filter isValid
|> execution
解决方案示例 #1
有一个内联的副作用,我需要类似的东西:
inputs
|> Seq.filter (fun (name,value) ->
let valid = isValid (name,value)
// side-effect
if not valid then
printfn "Invalid argument %s" name
valid
|> execution
解决方案示例#2
我可以使用元组进行更纯粹的关注点分离,但需要第二次迭代:
let validationResults =
inputs
// initial iteration
|> Seq.filter (fun (name,value) ->
let valid = isValid (name,value)
(name,value,valid)
|> execution
// one example of a 2nd iteration...
validationResults
|> Seq.filter (fun (_,_,valid) -> not valid)
|> Seq.map (fun (name,_,_) -> printfn "Invalid argument %s" name)
|> ignore
// another example of a 2nd iteration...
for validationResult in validationResults do
if not valid then
printfn "Invalid argument %s" name
每个答案更新 2014-07-23
我用这个作为每个答案的解决方案。该模式是使用包含条件的聚合函数。可能有更优雅简洁的方式来表达这一点......
open System
let inputs = [("name","my name");("number","123456");("invalid","")]
let isValidValue (name,value) =
not (String.IsNullOrWhiteSpace(value))
let logInvalidArg (name,value) =
printfn "Invalid argument %s" name
let execution (name,value) =
printfn "Valid argument %s: %s" name value
let inputPipeline input =
match isValidValue input with
| true -> execution input
| false -> logInvalidArg input
inputs |> Seq.iter inputPipeline
【问题讨论】:
-
此答案专门回答有关日志记录的问题,但应该与您的问题足够接近,以便您可以从中推断出一般解决方案:stackoverflow.com/a/24756810/126014
标签: f# functional-programming theory