How to avoid multiple iterations as a template?

In functional languages ​​(using F #), I try my best to find a balance between the advantages of a functional composition with one responsibility and getting the performance of one iteration over sequences. Any suggestions / examples of code templates to achieve both?

I don’t have a solid background in computational theory, and again and again I come across this general pattern: Iterating over a collection and the desire to do side effects during iteration to avoid further iterations over the same collection or its set of results.

A typical example is the “reduce” or “filter” function: during filtering, there are many times that I want to take an extra step based on the result of the filter, but I would like to avoid a second listing of the filtered results.

Let the input check be performed as a simple task:

  • Named input array
  • Connected to isValid function filter
  • Side effect: register invalid input names
  • Entering the correct entries for further execution

Problem example

In F #, I could first write:

inputs
// how to log invalid or other side-effects without messing up isValid??
|> Seq.filter isValid
|> execution

Solution Example # 1

With a threaded side effect, I need something like:

inputs
|> Seq.filter (fun (name,value) -> 
    let valid = isValid (name,value)
    // side-effect
    if not valid then
        printfn "Invalid argument %s" name
    valid
|> execution

Solution Example 2

I could use tuples for a cleaner separation of problems, but requiring a second iteration:

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

Update 2014-07-23 per reply

. , . , , ...

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
+4
2

F # , :

let log f (name, value) =
    let valid = f (name, value)
    if not valid then
        printfn "Invalid argument %s" name
    valid

​​:

f:(string * 'a -> bool) -> name:string * value:'a -> bool

, "real" isValid :

inputs
|> Seq.filter (log isValid)
|> execution

isValid name:'a * value:int -> bool, f log, , .

+5

( ), , , :

let valid, invalid = Array.partition isValid inputs
for name, _ in invalid do printfn "Invalid argument %s" name
execution valid
+2

All Articles