F# 比较与数组关联的函数
F# Compare function associated to arrays
此 post 与 有关。
我相信 F# 在某处存储了一种允许比较相同类型数组的方法,前提是数组的元素是可比较的。我之所以这么认为:Set 类型要求其元素是可比较的,而 Set 接受可比较类型的数组(它甚至在实例化时对它们的元素进行排序,让我们可以猜测它用于数组的比较函数)。几个例子:
let s1 = [| [| 10; 20 |]; [| 10; 19 |]; [| 10; 19; 100 |]; [| 10; 20; -100 |] |] |> Set.ofArray;;
returns
val s1 : Set<int []> = set [[|10; 19|]; [|10; 20|]; [|10; 19; 100|]; [|10; 20; -100|]]
同样,
let s2 = [| [| ("b", 1); ("a", 2) |]; [| ("z", 1) |]; [| ("a", 0); ("a", 0); ("a", 0) |]; [| ("b", 1); ("a", 3) |] |] |> Set.ofArray;;
returns
val s2 : Set<(string * int) []> = set [[|("z", 1)|]; [|("b", 1); ("a", 2)|]; [|("b", 1); ("a", 3)|]; [|("a", 0); ("a", 0); ("a", 0)|]]
(如果两个数组具有相同的长度,比较似乎是从左到右逐个元素比较,或者 arr1 > arr2 如果 arr1.Length > arr2.Length)
最后,更复杂的例子:
type Foo = Z of int | A of int
let s3 = [| [| Z 1; A 1|]; [| A 100 |]; [| Z 1; Z 20|]; [| Z 0; Z 0; Z 0 |]; [| Z 1; Z 10|] |] |> Set.ofArray;;
returns
val s3 : Set<Foo []> = set [[|A 100|]; [|Z 1; Z 10|]; [|Z 1; Z 20|]; [|Z 1; A 1|]; [|Z 0; Z 0; Z 0|]]`
但是最后一个示例不起作用,因为对象不可比较:
let s0 = [| [| box 10; box 20 |]; [| box 10; box 19 |] |] |> Set.ofArray;;
returns
stdin(52,62): error FS0001: The type 'obj' does not support the 'comparison' constraint. For example, it does not support the 'System.IComparable' interface`
所以我希望以上说明 Set 知道如何比较数组(前提是它们的元素类型是可比较的)。
不幸的是我无法直接访问两个数组的比较方法:
let x1 : int[] = [| 1 |]
let x2 : int[] = [| 2 |]
let c12 = x1.CompareTo(x2);;
给出以下错误信息:
let c12 = x1.CompareTo(x2);;
-------------^^^^^^^^^
stdin(3,14): error FS0039: The field, constructor or member 'CompareTo' is not defined.
我的问题:如何访问关联到数组对象的 CompareTo 方法? ... 如果可能,通过函数签名 arrcompare : arr1:obj -> arr2:obj -> int
或 arrcompare : arr1:obj -> arr2:obj -> int option
(当 2 个参数不是数组或不是同一类型的数组时使用 option
)。
您可以只使用相等运算符:(=)
数组不实现 System.IComparable
,而集合实现。这并不意味着不能比较数组。 FSharp 确实提供了类似于真正结构比较的数组比较,按等级、长度和元素进行比较。
[|1|] < [|2|]
// val it : bool = true
此行为在 F# 规范中描述,在标题 Equality, Hashing,and Comparison 下,伪代码:
// Special types not supporting IComparable
| (:? Array as arr1), (:? Array as arr2) ->
... compare the arrays by rank, lengths and elements ...
您的问题似乎源于试图比较基本上无法比较的事物,因为它们不是同一类型。也许您应该重新考虑您的方法,而不是针对各种不兼容数组的单独接口实现,您可以创建一个求和类型并在其上实现 System.IComparable
。
嗯,比较两个 IComparable
非常简单。
其余的是对象的集合,IEnumerable
应该占 Array
、IList
、ICollection
,我们可能可以按顺序进行比较。
其余的是元组,如果它们不实现 IEnumerable
,则有望实现 ITuple
。
let rec compareAny (o1 : obj) (o2:obj) =
match (o1, o2) with
| (:? IComparable as o1), (:? IComparable as o2)
-> Some(compare o1 o2)
| (:? IEnumerable as arr1), (:? IEnumerable as arr2) ->
Seq.zip (arr1 |> Seq.cast) (arr2 |> Seq.cast)
|> Seq.choose(fun (a, b) -> compareAny a b)
|> Seq.skipWhile ((=) 0)
|> Seq.tryHead
|> Option.defaultValue 0
|> Some
| (:? ITuple as tup1), (:? ITuple as tup2) ->
let tupleToSeq (tuple: ITuple) =
seq { for i in 0..tuple.Length do yield tuple.[i] }
compareAny (tupleToSeq tup1) (tupleToSeq tup2)
| _ -> None
IEnumerable
比较的解释如下:
从两者中取元素 |
如果两者都不可比较,则跳过,否则比较 |
忽略所有成功的平等测试 |
找到比较为 <
或 >
的第一个元素 |如果有 none(例如,一个空列表,return 0)|包装成一些。
多亏了 Asti 和 Kaefer 的回答,我得以深入挖掘并意识到我需要的是结构相等性,可以这样编码:
let rec structcompare (o1 : obj) (o2:obj) : int option =
match (o1, o2) with
| (:? IStructuralComparable as arr1), (:? IStructuralComparable as arr2) ->
if arr1.GetType() = arr2.GetType() then compare arr1 arr2 |> Some else None
| _ -> None
结果如下:
structcompare (box [| 10; 19 |]) (box [| 10; 20 |]);;
returns val it : int option = Some -1
structcompare (box [| 10; 19; 0 |]) (box [| 10; 20 |]);;
returns val it : int option = Some 1
和
structcompare (box [| 1 |]) (box [| "a" |]);;
returns val it : int option = None
此 post 与
我相信 F# 在某处存储了一种允许比较相同类型数组的方法,前提是数组的元素是可比较的。我之所以这么认为:Set 类型要求其元素是可比较的,而 Set 接受可比较类型的数组(它甚至在实例化时对它们的元素进行排序,让我们可以猜测它用于数组的比较函数)。几个例子:
let s1 = [| [| 10; 20 |]; [| 10; 19 |]; [| 10; 19; 100 |]; [| 10; 20; -100 |] |] |> Set.ofArray;;
returns
val s1 : Set<int []> = set [[|10; 19|]; [|10; 20|]; [|10; 19; 100|]; [|10; 20; -100|]]
同样,
let s2 = [| [| ("b", 1); ("a", 2) |]; [| ("z", 1) |]; [| ("a", 0); ("a", 0); ("a", 0) |]; [| ("b", 1); ("a", 3) |] |] |> Set.ofArray;;
returns
val s2 : Set<(string * int) []> = set [[|("z", 1)|]; [|("b", 1); ("a", 2)|]; [|("b", 1); ("a", 3)|]; [|("a", 0); ("a", 0); ("a", 0)|]]
(如果两个数组具有相同的长度,比较似乎是从左到右逐个元素比较,或者 arr1 > arr2 如果 arr1.Length > arr2.Length)
最后,更复杂的例子:
type Foo = Z of int | A of int
let s3 = [| [| Z 1; A 1|]; [| A 100 |]; [| Z 1; Z 20|]; [| Z 0; Z 0; Z 0 |]; [| Z 1; Z 10|] |] |> Set.ofArray;;
returns
val s3 : Set<Foo []> = set [[|A 100|]; [|Z 1; Z 10|]; [|Z 1; Z 20|]; [|Z 1; A 1|]; [|Z 0; Z 0; Z 0|]]`
但是最后一个示例不起作用,因为对象不可比较:
let s0 = [| [| box 10; box 20 |]; [| box 10; box 19 |] |] |> Set.ofArray;;
returns
stdin(52,62): error FS0001: The type 'obj' does not support the 'comparison' constraint. For example, it does not support the 'System.IComparable' interface`
所以我希望以上说明 Set 知道如何比较数组(前提是它们的元素类型是可比较的)。
不幸的是我无法直接访问两个数组的比较方法:
let x1 : int[] = [| 1 |]
let x2 : int[] = [| 2 |]
let c12 = x1.CompareTo(x2);;
给出以下错误信息:
let c12 = x1.CompareTo(x2);;
-------------^^^^^^^^^
stdin(3,14): error FS0039: The field, constructor or member 'CompareTo' is not defined.
我的问题:如何访问关联到数组对象的 CompareTo 方法? ... 如果可能,通过函数签名 arrcompare : arr1:obj -> arr2:obj -> int
或 arrcompare : arr1:obj -> arr2:obj -> int option
(当 2 个参数不是数组或不是同一类型的数组时使用 option
)。
您可以只使用相等运算符:(=)
数组不实现 System.IComparable
,而集合实现。这并不意味着不能比较数组。 FSharp 确实提供了类似于真正结构比较的数组比较,按等级、长度和元素进行比较。
[|1|] < [|2|]
// val it : bool = true
此行为在 F# 规范中描述,在标题 Equality, Hashing,and Comparison 下,伪代码:
// Special types not supporting IComparable
| (:? Array as arr1), (:? Array as arr2) ->
... compare the arrays by rank, lengths and elements ...
您的问题似乎源于试图比较基本上无法比较的事物,因为它们不是同一类型。也许您应该重新考虑您的方法,而不是针对各种不兼容数组的单独接口实现,您可以创建一个求和类型并在其上实现 System.IComparable
。
嗯,比较两个 IComparable
非常简单。
其余的是对象的集合,IEnumerable
应该占 Array
、IList
、ICollection
,我们可能可以按顺序进行比较。
其余的是元组,如果它们不实现 IEnumerable
,则有望实现 ITuple
。
let rec compareAny (o1 : obj) (o2:obj) =
match (o1, o2) with
| (:? IComparable as o1), (:? IComparable as o2)
-> Some(compare o1 o2)
| (:? IEnumerable as arr1), (:? IEnumerable as arr2) ->
Seq.zip (arr1 |> Seq.cast) (arr2 |> Seq.cast)
|> Seq.choose(fun (a, b) -> compareAny a b)
|> Seq.skipWhile ((=) 0)
|> Seq.tryHead
|> Option.defaultValue 0
|> Some
| (:? ITuple as tup1), (:? ITuple as tup2) ->
let tupleToSeq (tuple: ITuple) =
seq { for i in 0..tuple.Length do yield tuple.[i] }
compareAny (tupleToSeq tup1) (tupleToSeq tup2)
| _ -> None
IEnumerable
比较的解释如下:
从两者中取元素 |
如果两者都不可比较,则跳过,否则比较 |
忽略所有成功的平等测试 |
找到比较为 <
或 >
的第一个元素 |如果有 none(例如,一个空列表,return 0)|包装成一些。
多亏了 Asti 和 Kaefer 的回答,我得以深入挖掘并意识到我需要的是结构相等性,可以这样编码:
let rec structcompare (o1 : obj) (o2:obj) : int option =
match (o1, o2) with
| (:? IStructuralComparable as arr1), (:? IStructuralComparable as arr2) ->
if arr1.GetType() = arr2.GetType() then compare arr1 arr2 |> Some else None
| _ -> None
结果如下:
structcompare (box [| 10; 19 |]) (box [| 10; 20 |]);;
returns val it : int option = Some -1
structcompare (box [| 10; 19; 0 |]) (box [| 10; 20 |]);;
returns val it : int option = Some 1
和
structcompare (box [| 1 |]) (box [| "a" |]);;
returns val it : int option = None