views:

116

answers:

2

I have a function that will parse the results of a DataReader, and I don't know how many items are returned, so I want to use a while..do loop to iterate over the reader, and the outcome should be a list of a certain type.

(fun(reader) ->
            [
                while reader.Read() do
                    new CityType(Id=(reader.GetInt32 0), Name=(reader.GetString 1), StateName=(reader.GetString 2))
            ])

This is what I tried, but the warning I get is:

This expression should have type 'unit', but has type 'CityType'. Use 'ignore' to discard the result of the expression, or 'let' 
to bind the result to a name.

So what is the best way to iterate over a DataReader and create a list?

+8  A: 

you can use list comprehension:

[
   while reader.Read() do
       let d =  new CityType(Id=(reader.GetInt32 0), Name=(reader.GetString 1), StateName=(reader.GetString 2))
       yield d
]

Here is one more example:

/// The squares of the first 10 integers
let squaresOfOneToTen = [ for x in 0..10 -> x*x ]

or

let squaresOfOneToTen = [ for x in 0..10 do yield x*x ]

You can also do it for sequences and arrays:

seq { 
   for i=1 to 9 do 
      for j=1 to 9 do
         yield (i,j,i*j)
}


[| for i=1 to 100 do yield i*i |]

you can also yield a sub sequence in a sequence(yield!), here is an example using Project Euler 31.

Yin Zhu
Thank you for your help. I forgot about the yield keyword.
James Black
+5  A: 

Just to provide some additional information that may explain what is going on here - there are two ways of creating lists in F# and in both cases, you're writing something in square braces [ ... ] (which may be a bit confusing at first!)

List literals are used when you have a few expressions (known number of explicitly written expressions) and want to create a list containing the results of evaluating these expressions. For example:

[ 1; 2; 3; ]       // list of constant expressions
[ 1+1; 2+2; 3+3 ]  // creates list [ 2; 4; 6 ]

List comprehensions are used when you have some code that does something and generates list of elements (by producing elements during the calculation). This is what Yin Zhu uses in his answer. Some examples are:

[ yield 1; ]           // generates singleton list [ 1 ] 
[ yield 1; yield 2; ]  // generates list with two elements
[ for x in 1 .. 10 do  // iterates loop 10 times
    yield x * x ]      // ... generates element during every iteration

Advanced features
You can also use yield! to generate multiple elements at once:

[ yield! [ 1; 2 ]      // generates a list containing [ 1; 2; 3; 4 ]
  yield! [ 3; 4 ] ]

This can be used for writing recursive functions, so you could rewrite your code using recursion instead of loop (this would be useful if you needed to keep some state, but as your code looks currently, while is perfectly fine!)

let rec loop reader = 
  [ if reader.Read() then
      yield new CityType(Id=(reader.GetInt32 0), Name=(reader.GetString 1), 
                         StateName=(reader.GetString 2))   
      yield! loop reader ]

This is a pattern that often appears in list comprehensions, so it is useful to know about it :-).

Tomas Petricek
Thank you very much for the additional information. The last section I expect will be very useful soon.
James Black