Teach me how can I improve my F# linq2sql seqences
here I'm using linq2sql but I think that I got problems with it.
and main problem is access by ID here in example I'm making 2 embedded for but I got very scary linq2 sql queries because I don't know is there some additional methods or ways to make it ...
member X.deltaArchive() = // Reacting on delta limits
seq { for a in db.ArchiveAnalogs do
for d in db.Deltas do
if a.ID = d.ID then
if a.Value > d.DeltaLimit then
yield d.AboveMessage
else if a.Value < d.DeltaLimit then
yield d.BelowMessage
} |> Array.ofSeq
So the complete question is : is there any way to make the same without using embedded cycles to find id conformity ?
Thank you.
Added :
using :
<# seq {for a in db.ArchiveAnalogs do
for d in db.Deltas do
if a.ID = d.ID then
if a.Value > d.DeltaLimit then
yield a.Date, d.AboveMessage
else if a.Value < d.DeltaLimit then
yield a.Date, d.BelowMessage}
#> |> query |> Array.ofSeq
got error :
The following construct was used in query but is not recognised by the F#-to-LINQ query translator:
Call (None,
System.Collections.Generic.IEnumerable`1[System.Tuple`2[System.DateTime,System.String]] Singleton[Tuple`2](System.Tuple`2[System.DateTime,System.String]),
[NewTuple (PropertyGet (Some (a), System.DateTime Date, []),
PropertyGet (Some (d), System.String AboveMessage, []))])
This is not a valid query expression. Check the specification of permitted queries and consider moving some of the query out of the quotation
offtopic : I must find solution because this is first google link about "F# linq2sql"
First of all, the snippet you wrote isn't really using LINQ to SQL. You're running the whole processing in memory, because F# doesn't select query operators based on type (as C# does). You need to mark the query explicitly to run it on SQL:
#r "FSharp.PowerPack.Linq.dll"
open Microsoft.FSharp.Linq
<# seq { for a in db.ArchiveAnalogs do ... } #> |> query
An alternative way to write what you want is to use Query.join function (from PowerPack). I believe the following should do the trick:
<# join db.ArchiveAnalogs db.Deltas (fun a -> a.ID) (fun d -> d.ID) (fun a d ->
if a.Value > d.DeltaLimit then
yield d.AboveMessage
else if a.Value < d.DeltaLimit then
yield d.BelowMessage ) #> |> query
(Although, I think that there is really no difference between using join and nested for - If you run this on SQL than it will likely optimize it to join anyway).
Related
I have 2 different very simple functions with the same input-output structure (Both return a count(*) when avg of 3 notes is >= 4 (function1) and the other a count(*) when avg of 3 notes is < 4 (function2)), They both work properly in separate but now i need to join both into just one function with 2 outputs, I now maybe is a very easy question but i am only getting started with Haskell:
function1::[(String, Int,Int,Int)]->Int
function1 ((name,note1,note2,note3):xs) =
if (note1+note2+note3) `div` 3 >=4 then length xs else length xs
function2::[(String, Int,Int,Int)]->Int
function2 ((name,note1,note2,note3):xs) =
if (note1+note2+note3) `div` 3 <4 then length xs else length xs
Thanks!
You can use &&& from Control.Arrow.
combineFunctions f1 f2 = f1 &&& f2
Then use it like this :
combinedFunc = combineFunctions function1 function2
(res1,res2) = combinedFunc sharedArg
You already use tuples (name,note1,note2,note3) in your input data, so you must be familiar with the concept.
The simplest way to produce two outputs simultaneously is to put the two into one tuple:
combinedFunction f1 f2 input = (out1, out2)
where
out1 = f1 input
out2 = f2 input
It so happens that this can be written shorter as combinedFunction f1 f2 = f1 &&& f2 and even combinedFunction = (&&&), but that's less important for now.
A more interesting way to produce two outputs simultaneously is to redefine what it means to produce an output:
combinedFunWith k f1 f2 input = k out1 out2
where
out1 = f1 input
out2 = f2 input
Here instead of just returning them in a tuple, we pass them as arguments to some other user-specified function k. Let it decide what to do with the two outputs!
As can also be readily seen, our first version can be expressed with the second, as combinedFunction = combinedFunWith (,), so the second one seems to be more general ((,) is just a shorter way of writing a function foo x y = (x,y), without giving it a name).
First off, I would like to make you all aware that I'm very new to Haskell, so to increase knowledge etc, I've been trying out questions and I'm pretty stuck on one. I think I'm nearly there but some more experienced advice would be appreciated. Here's the question:
A sporting team is represented by it's name and the amount of points they scored in their last games like so ("Newcastle",[3,3,3,0]). This data is modelled by the type definitions:
type TName = String
type Points = [Int]
type Team = (TName,Points)
From this I have to define the following function that orders one team higher than another if their points sum is greater:
sortPoints :: [Team] -> [Team]
This is what I've tried:
sortPoints :: [Team] -> [Team]
sortPoints [_,()] -> []
sortPoints [_,(x:xs)] = sum[x|x<-xs]
Once I get to here, I'm not too sure how to go about adding the conditions to check the points sum, any pointers would be greatly appreciated as I'm still coming to terms with a lot of Haskell features.
Note: This post is written in literate Haskell. You can save it as Team.lhs and try it. That being said, it's basically a longer version of Carsten's comment. If you still try to figure things out, use hoogle and look for the functions, although it's fine if you manage things with sortBy solely first.
First of all, we're going to work on lists, so you want to import Data.List.
> module Team where
> import Data.List
It contains a function called sortBy:
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
The first argument of sortBy should be a function that compares two elements of your list and returns
LT if the first is less than the second,
EQ if the both are equal,
GT if the first is greater than the second.
So we need something that that takes two teams and returns their ordering:
> -- Repeating your types for completeness
> type TName = String
> type Points = [Int]
> type Team = (TName, Points)
>
> compareTeams :: Team -> Team -> Ordering
Now, you want to compare teams based on their sum of their points. You don't need their name, so you can capture just the second part of the pair:
> compareTeams (_, s1) (_, s2) =
We need the sum of the points, so we define sum1 and sum2 to be the respective sums of the teams:
> let sum1 = sum s1
> sum2 = sum s2
Now we can compare those sums:
in if sum1 < sum2
then LT
else if sum1 == sum2
then EQ
else GT
However, that's rather verbose, and there's already a function that has type Ord a => a -> a -> Ordering. It's called compare and part of the Prelude:
> in sum1 `compare` sum2
That's a lot more concise. Now we can define sortTeams easily:
> sortTeams :: [Team] -> [Team]
> sortTeams = sortBy compareTeams
And that's it, we're done!
Fine, I lied, we're not 100% done. The module Data.Ord contains a function called comparing that's rather handy:
comparing :: Ord b => (a -> b) -> a -> a -> Ordering
comparing f x y = f x `compare` f y -- or similar
Together with snd and sum you can define sortTeams in a single line:
sortTeams = sortBy (comparing $ sum . snd)
The alternative on mentioned by Carsten is on from Data.Function:
sortTeams = sortBy (compare `on` sum . snd)
I'm reviewing an old exam in my Haskell programming course and I can't seem to wrap my head around this function (I think there is just too little information given).
The code given is
myId x = x
function n f
| n > 0 = f . function (n-1) f
| otherwise = myId
I know that if I for example call the function with the input 2 (*2), I will get a function as result. And if I call it with (-2) (*2) 1 I will get the result 1.
I just don't know how? Also I can't wrap my head around the typecast of the function.
I know that these two options are correct but I don't understand why (probably parentheses that confuse me at the moment).
function :: (Num a, Ord a) => a -> (a -> a) -> a -> a
function :: (Num a, Ord b) => a -> (b -> b) -> b -> b
Anyone that can clarify how I should "read" this function and how I should understand how the typecast works (been reading my Programming in Haskell literature and from Learn You a Haskell but been going in circle for a few days now).
function takes some number n and a function f :: a -> a, and composes that function with itself n times, returning another function of type a -> a. When the returned function is applied to a value of type a, the result is essentially the same as executing f in a loop n times, using the output of each previous step as the input for the next.
Perhaps it is easier to see the similarity if the final parameter is made explicit:
function :: (Ord a, Num a) -> a -> (b -> b) -> b -> b
function n f x
| n > 0 = f (function (n-1) f x)
| otherwise = x
This is functionally equivalent to your point-free function.
In Haskell, a function f :: a -> b -> c can be interpreted either as "a function that takes an a and a b and returns a c" or "a function that takes an a and returns a function from b to c". When you apply a function to one or more inputs, think of each input as eliminating one of the function's arguments. In this instance, function 10 returns a new function with type (a -> a) -> a -> a, and function 2 (*2) returns a function with type Num a => a -> a.
When you think of it this way, it should hopefully be clear why function (-2) (*2) 1 returns a number while function 2 (*2) returns a function. There is no type casting going on; when you are applying the three argument function to two inputs, you get another function back instead of a value, since you didn't provide the final input needed to compute that value.
<#[for i in linq.TrueIncidents -> i.RecTime, i.Name ] #> |> query |> Array.ofSeq
How can I get counts of different names ?
just count(Name) where Name = somename...
I think first I must select form here all Names with |> Seq.distinctBy(fun x -> x.Name)
and then make Seq.Count() different selects where Name will be one of names and then union all the selects ... really wierd way.
Or I can use it as object later with closure with int ref counters for each distincted Name...
I understand that my explanation could be some messy , so ask if you can't get it. I want to know is there any way to use Count(Name) where Name = OneOfNames inside closure or linq2sql ?
I can't compile to verify that this works at the moment, but try the following:
<# linq.TrueIncidents
|> Seq.groupBy (fun i -> i.Name)
|> Seq.map (fun (name, is') -> name, Seq.length is') #>
|> query
|> Map.ofSeq
This should give you a Map<string, int> of each name and its respective number of occurrences.
I already sent the bug to fsbugs#microsoft.com but I also added this link to letter for additional description, code highlighting, discussions and maybe someone find some way to avoid it, because I really like it and want to use.
Code :
<# seq {for a in db.ArchiveAnalogs do
for d in db.Deltas do
if a.ID = d.ID then
if a.Value > d.DeltaLimit then
yield a.Date, d.AboveMessage
else if a.Value < d.DeltaLimit then
yield a.Date, d.BelowMessage}
#> |> query |> Array.ofSeq
Same error messages with update :
<# seq {for a in db.ArchiveAnalogs do
for d in db.Deltas do
if a.ID = d.ID && a.Value > d.DeltaLimit then
yield a.Date, d.AboveMessage
elif a.ID = d.ID && a.Value < d.DeltaLimit then
yield a.Date, d.BelowMessage}
#> |> query |> Array.ofSeq
Error message :
The following construct was used in query but is not recognised by the
F#-to-LINQ query translator: Call
(None,
System.Collections.Generic.IEnumerable1[System.Tuple2[System.DateTime,System.String]]
Singleton[Tuple2](System.Tuple2[System.DateTime,System.String]),
[NewTuple (PropertyGet (Some (a), System.DateTime Date, []),
PropertyGet (Some (d), System.String AboveMessage,
[]))]) This is not a valid query
expression. Check the specification of
permitted queries and consider moving
some of the query out of the quotation
Fixed
Code :
let px =
query <|
<# seq { for cl in db.Dictionaries -> cl }
|> Seq.filter(fun x -> x.ID_Line = l1 || x.ID_Line = l2) #>
|> fun pquery ->
query <|
<# seq { for cd in db.DeltaCompares do
for cl1 in pquery do
if cd.IID1 = cl1.IID then
for cl2 in pquery do
if cd.IID2 = cl2.IID then
yield cl1
yield cl2 } #>
|> List.ofSeq
Same error with update :
let p =
[for cl in db.Dictionaries -> cl]
|> Seq.filter(fun x -> x.ID_Line = l1 || x.ID_Line = l2)
|> fun pquery ->
<# seq { for cd in db.DeltaCompares do
for cl1 in pquery do
for cl2 in pquery do
if cd.IID1 = cl1.IID && cd.IID2 = cl2.IID then
yield cl1, cl2 } #>
|> query |> Seq.collect(fun a -> [fst a; snd a])
Error message :
The following construct was used in query but is not
recognised by the F#-to-LINQ query
translator: Call (None,
System.Collections.Generic.IEnumerable`1[LinqBase.Dictionary]
SingletonDictionary,
[cl1]) This is not a valid query expression. Check the specification of
permitted queries and consider moving
some of the query out of the quotation
fixed
I'm not sure if I do it correct so I also ask you to confirm if this is a bug or not a bug
In the first case, I think the F#-to-LINQ translator may be failing on nested if. Have you tried: (...)
EDIT [Second attempt]: It could also fail because we're using if without else clause. What if you always return something using option type and then filter out None values (there may be a way to make it nicer, but let's start with this):
<# seq {for a in db.ArchiveAnalogs do
for d in db.Deltas do
yield
if a.ID = d.ID && a.Value > d.DeltaLimit then
Some(a.Date, d.AboveMessage)
elif a.ID = d.ID a.Value < d.DeltaLimit then
Some(a.Date, d.BelowMessage)
else None }
#> |> query |> Seq.choose id |> Array.ofSeq
In the second case, it may be failing because of the for nested in if. I'd try this (...)
EDIT: This is actually incorrect use of LINQ (and it wouldn't work in C# too). The problem is that you're collecting some data in memory (pquery) and then passing this as an input to the LINQ (so that it would have to send the data back to the SQL server.
You can try writing it like this (BTW: I think using |> fun x -> is a weird construct when you can write the same thing simply just using let):
let pquery = <# db.Dictionaries
|> Seq.filter(fun x -> x.ID_Line = l1 || x.ID_Line = l2) #>
let px =
<# seq { for cd in db.DeltaCompares do
for p in %pquery do ... } |> query
This is using quotation splicing. For more information about this feature, see my article (search for splicing).