简体   繁体   中英

Why output is <seq> and not int in F#?

This is my code:

 Csv.GetSample().Rows
 |> Seq.groupBy (fun row -> row.STATE,row.INCOME,row.CHILDREN)
 |> Seq.map (fun ((state,income,children),data) -> 
    let pctrepub = data|> Seq.map (fun row -> (100.0 - float row.otherparty)) 
    ((state,income,children),pctrepub))
 |> List.ofSeq

This is my ideal result:

   (("TX", "51522", "0"), 65.0);
   (("AL", "6481", "1"), 51.4);
   (("MO", "78921", "1"), 25.1);
   (("TN", "12000", "4"), 62.1);
   (("PA", "79850", "2"), 41.2);
   (("NY", "79215", "1"), 31.0);
   (("CA", "79045", "2"), 50.5);

This is what I am ending up with:

   (("TX", "51522", "0"), <seq>);
   (("AL", "6481", "1"), <seq>);
   (("MO", "78921", "1"), <seq>);
   (("TN", "12000", "4"), <seq>);
   (("PA", "79850", "2"), <seq>);
   (("NY", "79215", "1"), <seq>);
   (("CA", "79045", "2"), <seq>);

Why is the last row of values appearing as <seq> and how do I fix this?

The last element of the tuple is pctrepub , which is the result of a Seq.map call, and Seq.map returns a sequence, not a number. So it's absolutely no surprise that the last element of the tuple is a sequence.

As to how to fix this, there is not enough information: I see that you want to get number instead of sequences, but you're not saying where the numbers should come from, so I can't help you there.

You've mentioned elsewhere that you're a beginner at F# (started learning it eight days ago), so here's a quick guide to how to read the F# documentation that might help you answer some of these questions yourself. Let's look at the documentation for the Seq module . It lists a lot of functions that can be called on sequences: append , average , averageBy , filter , map , groupBy ... They all have different type signatures, and the type signature of each function is a clue to what it will do. Let's look at a few examples:

  • average : seq<^T> -> ^T - Returns the average of the elements in the sequence.
  • averageBy : ('T -> ^U) -> seq<'T> -> ^U - Returns the average of the results generated by applying the function to each element of the sequence.
  • map : ('T -> 'U) -> seq<'T> -> seq<'U> - Creates a new collection whose elements are the results of applying the given function to each of the elements of the collection.

First, average , since it's simplest. The -> arrow in the type signure tells you that this is a function. The input is left of the arrow, and the output is right of the arrow. So that type signature means this function takes a seq<^T> and returns an item of type ^T . The ^ or ' characters in front of the type name T mean that it's a generic type, which will resolve to whatever type you actually have when you run the code: int, string, a tuple, whatever. So T is just a placeholder name for whatever type you actually have: if you have ints, this is a seq<int> -> int function. If you have strings, this is a seq<string> -> string function, and so on. (The ^ character in front of the type means that it will be resolved at compile-time, whereas a ' character in front means it will be resolved at run-time. You won't need to worry about this distinction until you've had a lot more experience with F#, so for now, just treat either one of these as a flag that means "This is a generic type"). So the type signature of the average function tells you that it takes a sequence of values and returns a single value .

Now, averageBy . This is more complicated. First, there are more arrows here. This is related to a concept called currying , which is explained very well at the link so I won't dive into it. At the beginner level, you should think of whatever comes after the final arrow as being the output of the function, and all the rest of the types as being the inputs of the function (which will be separated by arrows). In other words, a type signature like int -> char -> string should be read as "this is a function that takes two inputs, an int first and a char second, and returns a string ."

But do you see the parentheses around the ('T -> ^U) bit in the averageBy type signature? Those parentheses mean just what they mean in math: read this part in isolation. So the first parameter of averageBy is something of type ('T -> ^U) . The second parameter of averageBy is of type seq<'T> , and the output is of type ^U . Now if you look at the type signature ('T -> ^U) , it has an arrow in it, so it's a function. Specifically, it's a function that takes a thing of generic type T and returns a thing of generic type U . (As I said earlier, don't worry about the ' vs. ^ distinction right now). The second parameter is a sequence of items of type T , and the output is of type U . That type signature, plus the name, gives you a clue as to how the function works: it will look through the sequence of things of type T , and for each one, it will call the function to convert that T thing into a thing of type U . Then it returns a single U value as the output: from the name, you can conclude that the single value it returns will be the average of all the U values produced by the 'T -> ^U function.

So the type signature ('T -> ^U) -> seq<'T> -> ^U means that the first argument is a function that takes a T and returns a U , the second argument is a sequence of items of type T , and the function returns a single item of type U .

Finally, let's look at map . Its type signature looks rather similar to averageBy : the first parameter is again a function that takes a T and converts it to a U . The second parameter is again a sequence of T items. But this time, instead of a single value, the result of map will be a sequence of U values.

And so, by looking at the documentation , we can see that if you don't want a sequence as output, Seq.map is the wrong thing to call. There's a bewildering list of functions in the Seq module, so you might feel a bit overwhelmed. But carefully looking at the type signatures, and understanding what each one means, is a big help in figuring out which one will do what you want with your data. And reading through https://fsharpforfunandprofit.com/posts/list-module-functions/ will be a big help to you too: F# carefully treats the List , Seq and Array modules as extremely similar to each other , including having almost all the same functions. So if you know what List.averageBy does, you will also know what Seq.averageBy and Array.averageBy do as well: the only difference will be in what type of collection they take as input. So even though that page talks about lists, its advice also applies to Seq as well.

Someone has already answered your question in the comments (just do Seq.map (fun row -> (row.STATE,row.INCOME,row.CHILDREN), 100.0 - float row.otherparty) and you should get the result you want), so I won't go into more details there. Hopefully the time I spent to write this will be useful to you in helping you understand things in more depth, so that you can answer your own questions in the future and won't have to be quite as reliant on asking on Stack Overflow (and then waiting, sometimes hours, for the answers).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM