diff --git a/src/FSharpAux/Array.fs b/src/FSharpAux/Array.fs
index c31eed0..a506934 100644
--- a/src/FSharpAux/Array.fs
+++ b/src/FSharpAux/Array.fs
@@ -24,7 +24,30 @@ module Array =
// else
// i <- i + 1
// found
-
+
+ ///
+ /// Builds a new collection whose elements are the results of applying the given function
+ /// to the corresponding quadruples from the four collections. The four input
+ /// arrays must have the same length, otherwise an ArgumentException is
+ /// raised.
+ ///
+ /// The function to transform the quadruples of the input elements.
+ /// The first input array.
+ /// The second input array.
+ /// The third input array.
+ /// The fourth input array.
+ /// Thrown when the input arrays differ in length.
+ /// Thrown when any of the input arrays is null.
+ /// The array of transformed elements.
+ let map4 (mapping : 'T -> 'T -> 'T -> 'T -> 'U) (array1 : 'T []) (array2 : 'T []) (array3 : 'T []) (array4 : 'T []) =
+ checkNonNull "array1" array1
+ checkNonNull "array2" array2
+ checkNonNull "array3" array3
+ checkNonNull "array4" array4
+ if array1.Length <> array2.Length || array1.Length <> array3.Length || array1.Length <> array4.Length then
+ failwithf "The input lists have different lengths.\n\tarray1.Length = %i; array2.Length = %i; array3.Length = %i; array4.Length = %i" array1.Length array2.Length array3.Length array4.Length
+ [|for i = 0 to array1.Length - 1 do yield mapping array1[i] array2[i] array3[i] array4[i]|]
+
/// Builds a new array that contains every element of the input array except for that on position index.
let removeIndex index (arr : 'T []) =
if index < arr.Length then
diff --git a/src/FSharpAux/List.fs b/src/FSharpAux/List.fs
index 104aa36..04da638 100644
--- a/src/FSharpAux/List.fs
+++ b/src/FSharpAux/List.fs
@@ -4,7 +4,27 @@ open Microsoft.FSharp.Core.OptimizedClosures
[]
module List =
-
+
+ ///
+ /// Builds a new collection whose elements are the results of applying the given function
+ /// to the corresponding elements of the four collections simultaneously.
+ ///
+ /// The function to transform quadruples of elements from the input lists.
+ /// The first input list.
+ /// The second input list.
+ /// The third input list.
+ /// The fourth input list.
+ /// The list of transformed elements.
+ let map4 (mapping : 'T -> 'T -> 'T -> 'T -> 'U) (list1 : 'T list) (list2 : 'T list) (list3 : 'T list) (list4 : 'T list) =
+ if list1.Length <> list2.Length || list1.Length <> list3.Length || list1.Length <> list4.Length then
+ failwithf "The input lists have different lengths.\n\tlist1.Length = %i; list2.Length = %i; list3.Length = %i; list4.Length = %i" list1.Length list2.Length list3.Length list4.Length
+ let rec loop acc nl1 nl2 nl3 nl4 =
+ match nl1, nl2, nl3, nl4 with
+ | h1 :: t1, h2 :: t2, h3 :: t3, h4 :: t4 ->
+ loop (mapping h1 h2 h3 h4 :: acc) t1 t2 t3 t4
+ | _ -> List.rev acc
+ loop [] list1 list2 list3 list4
+
/// Applies a function to each element of the list, threading an accumulator argument through the computation. If the input function is f and the elements are i0...iN then computes f (... (f i0 i1)...) iN and returns the intermediary and final results. Raises ArgumentException if the list has size zero.
let scanReduce f l =
match l with
diff --git a/src/FSharpAux/Seq.fs b/src/FSharpAux/Seq.fs
index de72694..6b5d818 100644
--- a/src/FSharpAux/Seq.fs
+++ b/src/FSharpAux/Seq.fs
@@ -3,13 +3,17 @@
open System.Collections.Generic
[]
-module Seq =
-
- ///Adds a value to the back of a sequence.
+module Seq =
+
+ let inline internal checkNonNull argName arg =
+ if isNull arg then
+ nullArg argName
+
+ /// Adds a value to the back of a sequence.
let appendSingleton (s : seq<'T>) (value : 'T) =
Seq.append s (Seq.singleton value)
- ///Adds a value to the front of a sequence.
+ /// Adds a value to the front of a sequence.
let consSingleton (s : seq<'T>) (value : 'T) =
Seq.append (Seq.singleton value) s
@@ -222,17 +226,42 @@ module Seq =
-
-
+
/// Returns head of a seq as option or None if seq is empty.
let tryHead s = Seq.tryPick Some s
-
+
/// Returns head of a seq or default value if seq is empty.
let headOrDefault defaultValue s =
match (tryHead s) with
| Some x -> x
| None -> defaultValue
+ ///
+ /// Builds a new collection whose elements are the results of applying the given function
+ /// to the corresponding quadruples of elements from the four sequences. If one input sequence if shorter than
+ /// the others then the remaining elements of the longer sequences are ignored.
+ ///
+ /// The function to transform quadruples of elements from the input sequences.
+ /// The first input sequence.
+ /// The second input sequence.
+ /// The third input sequence.
+ /// The fourth input sequence.
+ /// The result sequence.
+ /// Thrown when any of the input sequences is null.
+ let map4 (mapping : 'T -> 'T -> 'T -> 'T -> 'U) (source1 : seq<'T>) (source2 : seq<'T>) (source3 : seq<'T>) (source4 : seq<'T>) =
+ checkNonNull "source1" source1
+ checkNonNull "source2" source2
+ checkNonNull "source3" source3
+ checkNonNull "source4" source4
+ let e1 = source1.GetEnumerator()
+ let e2 = source2.GetEnumerator()
+ let e3 = source3.GetEnumerator()
+ let e4 = source4.GetEnumerator()
+ seq {
+ while e1.MoveNext() && e2.MoveNext() && e3.MoveNext() && e4.MoveNext() do
+ yield mapping e1.Current e2.Current e3.Current e4.Current
+ }
+
/// Splits a sequence of pairs into two sequences.
let unzip (input : seq<_>) =
let (lstA, lstB) =
diff --git a/tests/FSharpAux.Tests/ArrayTests.fs b/tests/FSharpAux.Tests/ArrayTests.fs
index 37ed397..1952d1b 100644
--- a/tests/FSharpAux.Tests/ArrayTests.fs
+++ b/tests/FSharpAux.Tests/ArrayTests.fs
@@ -6,6 +6,8 @@ open Expecto
let testArray1 = [|1337; 14; 23; 23; 69; 1; 2; 3; 1000; 9001; 23|]
let testArray2 = [|3; 3; 2; 4; 2; 1|]
let testArray3 = [|6; 6; 2; 4; 2; 8|]
+let testArray4 = [|6; 6; 2; 4; 2; 9|]
+let testArray5 = [|6; 6; 2; 4; 2; 5|]
let testArray1_filteri_Equal = [|14; 23; 23; 69|]
let testArray1_filteri_NotEqual = [|1337; 14; 23;|]
@@ -21,10 +23,20 @@ let testArray1_skipNth_Equal = [|1337; 14; 23; 69; 2; 3; 9001; 23|]
let testArray1_skipNth_NotEqual = [|5; 6; 7; 10|]
let testArray1_groupWhen_Equal = [|[|1337; 14|]; [|23|]; [|23|]; [|69|]; [|1; 2|]; [|3; 1000|]; [|9001|]; [|23|]|]
let testArray1_groupWhen_NotEqual = [|[|1337; 14|]; [|23|]; [|23|]; [|69|]; [|1; 2|]; [|3; 1000|]; [|9001; 23|]|]
+let testArray_map4 = [|(3, 6, 6, 6); (3, 6, 6, 6); (2, 2, 2, 2); (4, 4, 4, 4); (2, 2, 2, 2); (1, 8, 9, 5)|]
[]
let arrayTests =
testList "ArrayTests" [
+ testList "Array.map4" [
+ testCase "Throws when any array is null" <| fun _ ->
+ Expect.throws (fun _ -> Array.map4 (fun _ _ _ _ -> ()) testArray2 testArray3 null null |> ignore) "Array.map4 did not throw when an input array was null"
+ testCase "Throws when arrays have unequal lengths" <| fun _ ->
+ Expect.throws (fun _ -> Array.map4 (fun _ _ _ _ -> ()) [|1|] [||] [|3|] [|4|] |> ignore) "Array.map4 did not throw when input arrays have unequal length"
+ testCase "Maps correctly" <| fun _ ->
+ let res = Array.map4 (fun a b c d -> a, b, c, d) testArray2 testArray3 testArray4 testArray5
+ Expect.sequenceEqual res testArray_map4 "Array.map4 did not map correctly"
+ ]
testList "Array.filteri" [
testCase "returns correct array" (fun _ ->
Expect.equal (testArray1 |> Array.filteri (fun i t -> i < 5 && t < 100)) testArray1_filteri_Equal "Array.filteri did return correct array"
diff --git a/tests/FSharpAux.Tests/ListTests.fs b/tests/FSharpAux.Tests/ListTests.fs
index a0ed0d0..4153647 100644
--- a/tests/FSharpAux.Tests/ListTests.fs
+++ b/tests/FSharpAux.Tests/ListTests.fs
@@ -32,10 +32,18 @@ let testList4_groupWhen_Equal = [[3]; [3; 2; 4; 2; 2]]
let testList4_groupWhen_NotEqual = [[3]; [3; 2; 4; 2]; [2]]
let testList5_groupWhen_Equal = [[3]; [3; 2; 4; 2]; [1]]
let testList5_groupWhen_NotEqual = [[3]; [3; 2; 4; 2; 1]]
+let testList_map4 = [(3, 3, 3, 3); (3, 3, 3, 3); (2, 2, 2, 2); (4, 4, 4, 4); (1, 1, 2, 2); (2, 1, 2, 1)]
[]
let listTests =
testList "ListTests" [
+ testList "List.map4" [
+ testCase "throws when lists have different lengths" <| fun _ ->
+ Expect.throws (fun _ -> List.map4 (fun _ _ _ _ -> ()) [1] [2] [3; 3] [4] |> ignore) "List.map4 did not throw when lists had different lengths"
+ testCase "maps correctly" <| fun _ ->
+ let res = List.map4 (fun a b c d -> a, b, c, d) testList2 testList3 testList4 testList5
+ Expect.sequenceEqual res testList_map4 "List.map4 did not map lists correctly"
+ ]
testList "List.filteri" [
testCase "returns correct list" (fun _ ->
Expect.equal (testList1 |> List.filteri (fun i t -> i < 5 && t < 100)) testList1_filteri_Equal "List.filteri did return correct List"
diff --git a/tests/FSharpAux.Tests/SeqTests.fs b/tests/FSharpAux.Tests/SeqTests.fs
index d048bd1..2f79ac6 100644
--- a/tests/FSharpAux.Tests/SeqTests.fs
+++ b/tests/FSharpAux.Tests/SeqTests.fs
@@ -18,6 +18,8 @@ let testSeq4_groupWhen_Equal = seq {seq {3}; seq {3; 2; 4; 2; 2}}
let testSeq4_groupWhen_NotEqual = seq {seq {3}; seq {3; 2; 4; 2}; seq {2}}
let testSeq5_groupWhen_Equal = seq {seq {3}; seq {3; 2; 4; 2}; seq {1}}
let testSeq5_groupWhen_NotEqual = seq {seq {3}; seq {3; 2; 4; 2; 1}}
+let testSeq_map4_1 = seq {(3, 3, 3, 3); (3, 3, 3, 3); (2, 2, 2, 2); (4, 4, 4, 4); (1, 1, 2, 2); (2, 1, 2, 1)}
+let testSeq_map4_2 = seq {(3, 3, 3, 1337); (3, 3, 3, 14); (2, 2, 2, 23); (4, 4, 4, 23); (1, 1, 2, 69); (2, 1, 2, 1)}
// helper functions
let list s = Seq.toList s
@@ -26,6 +28,16 @@ let list2 s = Seq.map (Seq.toList) s |> Seq.toList
[]
let seqTests =
testList "SeqTests" [
+ testList "Seq.map4" [
+ testCase "throws when any seq is null" <| fun _ ->
+ Expect.throws (fun _ -> Seq.map4 (fun _ _ _ _ -> ()) testSeq2 testSeq2 null testSeq2 |> ignore) "Seq.map4 did not throw when any seq is null"
+ testCase "maps correctly" <| fun _ ->
+ let res = Seq.map4 (fun a b c d -> a, b, c, d) testSeq2 testSeq3 testSeq4 testSeq5 |> list
+ Expect.sequenceEqual res (list testSeq_map4_1) "Seq.map4 did not map seqs correctly"
+ testCase "maps correctly with unequal seq lengths" <| fun _ ->
+ let res = Seq.map4 (fun a b c d -> a, b, c, d) testSeq2 testSeq3 testSeq4 testSeq1 |> list
+ Expect.sequenceEqual res (list testSeq_map4_2) "Seq.map4 did not map correctly when seqs have unequal lengths"
+ ]
let isOdd = fun n -> n % 2 <> 0
testList "Seq.groupWhen" [
testCase "returns correct jagged list, case1: [3; 3; 2; 4; 1; 2]" (fun _ ->