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
 

let aArray = [| ("A", "+", 1)
                ("B", "+", 2)
                ("C", "+", 3)
                ("D", "+", 4)
                ("A", "+", 1)
                ("B", "-", 2)
                ("C", "-", 1) |]

                                        
let results = aArray 
              |> 
              Array.map ( fun (k, op,v) 
                                -> ( k, if op = "+" 
                                            then 
                                                v 
                                            else 
                                                -v) )
              |> Array.fold ( fun acc (k,v) -> 
                                    if acc|>List.exists ( fun item -> fst item = k ) then 
                                       let index = acc |> List.findIndex (fun item -> fst item = k)
                                       let foundItem = acc.[index]
                                       (k,v + snd foundItem) :: acc 
                                                |> List.filter (fun item -> item <> foundItem )
                                    else
                                        (k,v) :: acc
                              ) [] 
              |> List.sortBy ( fun item -> fst item ) 
              |> List.toArray
By on 12/26/2010 1:44 PM ()

marekb: There is a bug in your code: If you append ("D", "+", 0) to aArray, the result for D will be missing.

By on 12/27/2010 7:03 AM ()
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
let aArray = [| ("A", "+", 1)
                ("B", "+", 2)
                ("C", "+", 3)
                ("D", "+", 4)
                ("A", "+", 1)
                ("B", "-", 2)
                ("C", "-", 1)
                |]

                                        
let results = aArray 
              |> 
              Array.map ( fun (k, op,v) 
                                -> ( k, if op = "+" 
                                            then 
                                                v 
                                            else 
                                                -v) )
              |> Array.fold ( fun acc (k,v) -> 
                                    if acc|>List.exists ( fun item -> fst item = k ) then 
                                       let index = acc |> List.findIndex (fun item -> fst item = k)
                                       let foundItem = acc.[index]
                                       (k,v + snd foundItem) :: (List.filter (fun item -> item <> foundItem ) acc) 
                                                
                                    else
                                        (k,v) :: acc
                              ) [] 
              |> List.sortBy ( fun item -> fst item ) 
              |> List.toArray
By on 12/27/2010 1:39 PM ()

Hi, Joh:
I have a further question for this:
let a1 = [| ("A", "+", 1, 10.0M); ("B", "+", 2, -20.0M); ("C", "+", 3, 30.0M); ("D", "+", 4, 40.0M); ("A", "+", 1, 10.0M); ("B", "-", 2, 30.0M); ("C", "-", 1, -10.0M) |]
For the tuples in a1, if the first element is the same, then if the second element is "+", then do an add on third element,
if it is a "-", do a minus on the third element; also, add the 4th elements for all the same first element, to get the final result.
For the above example, I want this:
let aResult = [| ("A", 2, 20.0M); ("B", 0, 10.0M); ("C", 2, 20.0M); ("D", 4, 40.0M) |]
I found your code is rather difficult to understand, if you can provide a little explanation on this, it will be great!
Thanks and Happy New Year to you!

By on 12/27/2010 1:10 PM ()

zydjohn_2000:
I'll let you answer your new question yourself, it can be a good exercise to get you to understand the code I posted.
Regarding the explanation of my code, I would have to know what's your background first, otherwise it's a bit difficult to describe my code using concepts with which you are familiar.

Anyway, here is an alternate version, with comments this time:

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
(* A function which executes a sequence of operations.
   Each operation is represented by a tuple (var_name, op, arg).
   There are two operations, the first one increments the variable named
   var_name by the value provided in arg. The second performs a decrement.
   op denotes which operation is to be performed (increment or decrement).      
*)
let exec2 ops =
    // Mapping from variable names to values.
    // Keeps track of the value of each variable.
    // Variables not present in the dictionary have value 0.
    let values = dict Seq.empty
    
    // Execute each operation in ops, updating the dictionary at each step.
    for (var_name, op, n) in ops do
        // Get the value of the variable to be read and modified.
        let v =
            match values.TryGetValue(var_name) with
            | true, n -> n
            | false, _ -> 0
        
        // Depending on op, do an addition or a subtraction.
        match op with
        | "+" -> values.[var_name] <- (v + n)
        | "-" -> values.[var_name] <- (v - n)
        | _ -> failwith "Invalid operation"

    // Turn the dictionary into an array of key-value pairs.
    values
    |> Seq.map (fun kvp -> kvp.Key, kvp.Value)
    |> Array.ofSeq                

The main differences with my earlier version are:
- Use of a for loop with a mutable accumulator (called "values", in the code above). The first version used Array.fold instead.
- Use of a dictionary instead of a map. Dictionaries are a more natural choice for mutable maps, I believe. The first version used an immutable map instead.
- I did not try to avoid code duplication in "match op with...", which avoids using a function value.

By on 12/28/2010 2:45 AM ()

<!--[if gte mso 9]><xml>

<w:WordDocument>

<w:View>Normal</w:View>

<w:Zoom>0</w:Zoom>

<w:PunctuationKerning/>

<w:ValidateAgainstSchemas/>

<w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>

<w:IgnoreMixedContent>false</w:IgnoreMixedContent>

<w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>

<w:Compatibility>

<w:BreakWrappedTables/>

<w:SnapToGridInCell/>

<w:WrapTextWithPunct/>

<w:UseAsianBreakRules/>

<w:DontGrowAutofit/>

</w:Compatibility>

<w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel>

</w:WordDocument>

</xml><![endif]--><!--[if gte mso 9]><xml>

<w:LatentStyles DefLockedState="false" LatentStyleCount="156">

</w:LatentStyles>

</xml><![endif]--><!--[if gte mso 10]>

<style>

/* Style Definitions */

table.MsoNormalTable

{mso-style-name:"Table Normal";

mso-tstyle-rowband-size:0;

mso-tstyle-colband-size:0;

mso-style-noshow:yes;

mso-style-parent:"";

mso-padding-alt:0in 5.4pt 0in 5.4pt;

mso-para-margin:0in;

mso-para-margin-bottom:.0001pt;

mso-pagination:widow-orphan;

font-size:10.0pt;

font-family:"Times New Roman";

mso-ansi-language:#0400;

mso-fareast-language:#0400;

mso-bidi-language:#0400;}

</style>

<![endif]-->Hello, Joh:

Thank you very much for your explanation. I have tried your new code, but I got some

error:

>

let a1 = [| ("A", "+", 1); ("B", "+",

2); ("C", "+", 3); ("D", "+", 4);

("A", "+", 1); ("B", "-", 2);

("C", "-", 1) |]

let

exec2 ops =

let values = dict Seq.empty

for (var_name, op, n) in ops do

let v =

match values.TryGetValue(var_name)

with

| true, n -> n

| false, _ -> 0

match op with

| "+" -> values.[var_name]

<- (v + n)

| "-" -> values.[var_name]

<- (v - n)

| _ -> failwith "Invalid

operation"

values

|> Seq.map (fun kvp -> kvp.Key,

kvp.Value)

|> Array.ofSeq

let

a2 = exec2 a1;;

System.NotSupportedException:

Exception of type 'System.NotSupportedException' was thrown.

at

Microsoft.FSharp.Core.ExtraTopLevelOperators.CreateDictionary@45.set_Item(TKey

key, TValue value)

at FSI_0002.exec2[a](IEnumerable`1 ops)

at

<StartupCode$FSI_0002>.$FSI_0002.main@()

Stopped

due to error

I am thinking, if there is no indent issue with the above

code, then something is not correct with let values = dict Seq.empty; so I

tried this statement, and I got the error:

>

let values = dict Seq.empty;;

let values = dict Seq.empty;;

----^^^^^^

stdin(19,5):

error FS0030: Value restriction. The value 'values' has been inferred to have

generic type

val values :

System.Collections.Generic.IDictionary<'_a,'_b> when '_a : equality

Either

define 'values' as a simple data term, make it a function with explicit

arguments or, if you do not intend for it to be generic, add a type annotation.

I don't think I have good knowledge with F#, which is rather

new to me. However, I have 5+ years

experience with VB.NET, I think the imperative programming is easy to

understand, but some time is not easy to code.

One year ago, I found Microsoft will give another new programming language F# in

VS 2010, so I began to learn it, I have read the book "F# Expert" by

Don Sym, I found the concepts are rather good; however, due to the version

issues, more than half of the code in the book did NOT even compile in F# CTP;

this year, I got VS 2010, and trying to write some new programs using F#, the

simple issues, I can do now, but quite a lot of difficult issues I don't know.

For example, in this question, I understand the

"map" can work on the tuples with only 2 elements, but my real

problems have the tuples with 4 elements, if I have to use "map" on

more than 2-elements tuples, I am totally lost.

But your new codes seem to be much easier for me to

understand, however, I don't know how to get rid of the error message

first. If I can get this right, then I

will try to find the solution for my new question.

Thanks and Happy New Year to you.

By on 12/28/2010 8:37 AM ()

Sorry, the faulty line should have been:

1
let values = new System.Collections.Generic.Dictionary<_, _>()

dict creates an immutable dictionary, which was not what I wanted here.

By on 12/28/2010 9:09 AM ()

Hi, Joh:
Thank you very much, your code works now.
However, I am having the difficulties to do my further question.
As the target data is an array of tuples of 3 elements. I can see that you can create a dictionary which contains only 2 elements.
Unlike VB, most of the data are mutable, but in F#, most of the data is immutable; for array, I know it is semi-immutable, as I can change the contents in the array, but I have to give the length of the array when I initialize it, for dictionary, you can use new without specifying the length; but I don't know how I can initialize an array of tuples of 3 elements without specifying the length.
Let me know which data type you want to use, I believe the dictionary is not the choice.
Thanks for your help!

By on 12/28/2010 9:38 AM ()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let exec ops =
    ops
    |> Array.fold (fun values (var_name, op, n) ->
        let getValOrZero var_name =
            match Map.tryFind var_name values with
            | Some n -> n
            | None -> 0

        let op =
            match op with
            | "+" -> (+)
            | "-" -> (-)
            | _ -> failwith "Invalid operation"

        Map.add var_name (op (getValOrZero var_name) n) values
    ) Map.empty
    |> Map.toSeq
    |> Array.ofSeq
By on 12/26/2010 6:28 AM ()

Hi, Joh:
Thank you very much, your code works.
Happy New Year to you!
Thanks again for your help!

By on 12/26/2010 8:40 AM ()
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