The Seq module also provides a mapi function, this passes an index into the map function which allows you to tell if you are in the first or last interation:

1
Seq.mapi (fun i x -> if i = 0 then printfn "First!"; x + 1) [ 1 .. 0]
By on 3/7/2008 10:26 PM ()

Thanks! Might there also be a convenient way to know when you are at the last element? I made a couple of tries below.

Thanks,

Mike

P.S. Firefox would not allow me to reply, apparently some javascript was running out of control. So I had to use IE, and then had to use html to stop the editor from doubling all lines and destroying all identation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#light
let printElement i x =
    let printFirstElement x = printf "%A" x
    let printNextElement x = printf ", %A" x
    let printLastElement x = printfn ", %A" x 
    match i with 
    | 0 -> printFirstElement x
    | _ when x = 9 -> printLastElement x  // Ugh, hardcoding last element!  
    | _ -> printNextElement x

let naturalOdds = 
    let naturalNumbers = { 0 .. 10 }
    let isOdd i = i % 2 <> 0
    naturalNumbers  |> Seq.filter isOdd
    
let printNaturalOdds =         
    naturalOdds |> Seq.mapi (fun i x -> printElement i x)  
                |> Seq.to_list

// Showing off some recursion, but seems like a terrible way to go about
// getting a count.
let MaxSequenceElement sq = 
    let rec MaxListElement (lst : list<INT>) (i : int) : int =
        match lst with
        | h :: t -> MaxListElement t (i + 1)
        | [] -> i
    MaxListElement (Seq.to_list sq) 0   
    
let cnt = MaxSequenceElement naturalOdds
printfn "Number of odds: %d" cnt
                    
printNaturalOdds
By on 3/9/2008 11:38 AM ()

Hi,

I should have made it clear, all the code I posted does work. I was just looking for a more convenient way to know when you are at the last element of a sequence. The goal is to make it simple to add Start and End conditions when streaming through pipes.

Another question I have is how best to parse command line arguments. I heard in a podcast somewhere about an existing library, which I think may just be a holdover from another language and may not be supported/recommended in future.

Thanks,

Mike

By on 3/9/2008 1:10 PM ()

Hi Mike,

A quick fix is to test the number of elements in the list and use that to work out if your on the last item. Fine for the length of lists your dealing with, but if the data set is huge or even infinite then this is a problem as the Seq.length function needs walk each item in the list to count them:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#light

let printElement i x l =
    let printFirstElement x = printf "%A" x
    let printNextElement x = printf ", %A" x
    let printLastElement x = printfn ", %A" x 
    match i with 
    | 0 -> printFirstElement x
    | _ when x = l -> printLastElement x  // Ugh, hardcoding last element!  
    | _ -> printNextElement x

let naturalOdds = 
    let naturalNumbers = { 0 .. 20 }
    let isOdd i = i % 2 <> 0
    naturalNumbers  |> Seq.filter isOdd
    
let printNaturalOdds() =         
    let len = Seq.length naturalOdds
    naturalOdds |> Seq.iteri (fun i x -> printElement i x len)  

printNaturalOdds()

Also I fixed a couple of other minor problems printNaturalNumbers should have been a function, it was not in your implmentation. You should use Seq.iteri rather than Seq.mapi because here you are preforming a side effect.

Note than the F# interactive can already pretty print list/seq you can control the number of items printed via the fsi property.

Also note that F# built in list are slightly better for this sort of thing as your can easily tell whether you have the last item using pattern matching.

Cheers,
Rob

By on 3/10/2008 3:34 AM ()

Yes, you raise several good points. Also, I looked at the source and noticed that Seq.iteri is not lazy, while Seq.mapi is.

I tried creating a wrapper around Seq.iteri, in order to get "first" and "last" functionality, but the output was not as desired. The "first" and "last" functions get run immediately, before the sequence is iterated:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#light

// TODO: initiate a variable in "first", inc it in "next", 
// and finally print it in "last".
let MySeqIteri first (next : int -> int -> unit) last (e : seq) =
    first  // lazy (first) does not change the output.
    Seq.iteri (fun i x -> next i x) e
    last

let printFirst = printf ":First:"
let printNext i x = printf ", %A" x
let printLast = printfn ":Last:"

{ 1..20 } |> MySeqIteri printFirst printNext printLast

Outputs:

1
2
3
PS > ./PrintOdds2.exe
:First::Last:
, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20PS >

(I'm compiling and running from the Powershell command line.)
Any ideas?

This has been instructive and fun, thanks!

Mike

By on 3/10/2008 10:19 PM ()

Hi Mike,

Your miss understanding the the difference between a value and value that is a function. All function must have at least one argument, so if you don't want any arguments you use the unit type, which is represent by a pair of brackets: (). See how I've fixed your function:

1
2
3
4
5
6
7
8
9
10
11
#light
let MySeqIteri first (next : int -> int -> unit) last e =
    first()  
    Seq.iteri (fun i x -> next i x) e
    last()

let printFirst() = printf ":First:"
let printNext i x = printf ", %A" x
let printLast() = printfn ":Last:"

{ 1..20 } |> MySeqIteri printFirst printNext printLast

Here is what your really trying to do:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    #light
    let MySeqIteri first next last e =
        let lasti = (Seq.length e) - 1
        e |> Seq.iteri (fun i x ->
            if i = 0 then 
                first x
            else if i = lasti then
                last x
            else
                next x)
                

    let printFirst x = printf "%A" x
    let printNext x = printf ", %A" x
    let printLast x = printfn ", %A" x

    { 1..20 } |> MySeqIteri printFirst printNext printLast

Finally the syntax is a lot clean if you restrict yourself to F# build in lists, which support pattern matching:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    #light
    let rec MyListIteri first next last (l:list<_>) =
        if not l.IsNil then
            first (l.Head)
            let rec innerLoop l =
                match l with
                | hd :: [] -> last hd
                | hd :: tail -> 
                    next hd
                    innerLoop tail
                | _ -> ()
            innerLoop l.Tail

    let printFirst x = printf "%A" x
    let printNext x = printf ", %A" x
    let printLast x = printfn ", %A" x

    [ 1..20 ] |> MyListIteri printFirst printNext printLast
By on 3/11/2008 10:00 AM ()

Very instructive, thanks! Sequences do look simpler to my still imperative mind, and I think the version below most closely approximates what Powershell's "foreach" does, and seems to be the most generic overall.

Thanks again.

Mike

1
2
3
4
5
6
7
8
9
10
11
#light

let MySeqIteri first next last e =
    first() 
    Seq.iteri (fun i x -> next i x) e
    last()

let printFirst() = printfn "Some Numbers:"
let printNext i x = if i = 0 then printf "%A" x else printf ", %A" x
let printLast() = printfn ""; printfn "How dja like that?"
{ 1..20 } |> MySeqIteri printFirst printNext printLast
By on 3/11/2008 9:48 PM ()

Was just reading about Seq.fold and realized the example I gave was actually a folding problem:

1
2
3
4
5
let MyJoin acc (i : int) =
    if acc = "" then i.ToString()
    else acc + ", " + i.ToString()

{ 1 .. 20 } |> Seq.fold MyJoin "" |> printfn "%A"

Cool stuff!

Mike

By on 3/12/2008 2:47 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper