F中的词典排序#
我正在玩玩具问题(凸面船体识别),需要两次字典排序。其中一个案例得到了type Point = { X: float; Y: float }
的列表,我想用X坐标进行排序,如果相等,则用Y坐标进行排序。
我最后写了以下内容:F中的词典排序#
let rec lexiCompare comparers a b =
match comparers with
[ ] -> 0
| head :: tail ->
if not (head a b = 0) then head a b else
lexiCompare tail a b
let xComparer p1 p2 =
if p1.X > p2.X then 1 else
if p1.X < p2.X then -1 else
0
let yComparer p1 p2 =
if p1.Y > p2.Y then 1 else
if p1.Y < p2.Y then -1 else
0
let coordCompare =
lexiCompare [ yComparer; xComparer ]
,让我做
let lowest (points: Point list) =
List.sortWith coordCompare points
|> List.head
到目前为止,一切都很好。但是,这感觉有点过分。我必须创建特定的比较器返回-1,0或1,到目前为止,我看不到在List.minBy等情况下直接使用此方法的方法。理想情况下,我想按照提供可以比较的函数列表的方式做一些事情(比如[(fun p - > pX);(fun p - > pY)]),并执行列表中的词典列表min支持该功能列表的项目。
有没有办法在F#中实现这一点?或者我错误地考虑了这个问题?
有没有办法在F#中实现这一点?或者我错误地考虑了这个问题?
> type Point = { X: float; Y: float };; type Point = {X: float; Y: float;}
您可以立即开始比较值:当你定义一个记录类型像你
F#自动为您完成此。例如,定义的点的3元素的列表,并使用内置的List.sort
它分类到词典顺序:
> [ { X = 2.0; Y = 3.0 }
{ X = 2.0; Y = 2.0 }
{ X = 1.0; Y = 3.0 } ]
|> List.sort;;
val it : Point list = [{X = 1.0;
Y = 3.0;}; {X = 2.0;
Y = 2.0;}; {X = 2.0;
Y = 3.0;}]
注意,结果通过X
第一排序,然后通过Y
。
您可以使用内置的compare
函数比较任何可比类型的两个值。
如果您想使用自定义排序,那么您有两个选项。如果您想使用自定义总订单执行所有操作,那么它属于IComparable
和朋友的实现类型定义。如果您想要使用自定义排序进行一些操作,那么您可以使用更高阶的函数,如List.sortBy
和List.sortWith
。例如,List.sortBy (fun p -> p.Y, p.X)
将按Y
排序,然后按X
排序,因为F#会为您(!)生成2元组的字典对比。
这是F#的一大优点之一。
谢谢!它看起来像词典分类对超过2个元素的元组进行工作,所以这可以完成这项工作:按照所需的词典顺序将元素列表映射到元组,并将结果排序完美。 – Mathias 2012-04-12 02:36:36
“看起来像词典分类对超过2个元素的元组有影响”。绝对地,'compare'适用于所有可比较的F#类型,包括元组,记录和联合。所有你需要的代码都是为你自动生成的。这非常有用! – 2012-04-12 09:58:49
好了,开始用,你可以依靠内置compare
函数F#的:
let xComparer p1 p2 = compare p1.X p2.X
let yComparer p1 p2 = compare p1.Y p2.Y
或者,如果你愿意可以清楚地抽象此位:
let compareWith f a b = compare (f a) (f b)
let xComparer = compareWith (fun p -> p.X)
let yComparer = compareWith (fun p -> p.Y)
或者,如您注意到,您可以将此方法直接构建到列表处理函数中:
let rec lexiCompareWith l a b =
match l with
| [] -> 0
| f::fs ->
match compare (f a) (f b) with
| 0 -> lexiCompareWith fs a b
| n -> n
这里的一个重要限制是,既然你把它们放到一个列表中,这些函数都必须具有相同的返回类型。在Point
示例中这不是问题(因为这两个函数的类型都是Point -> float
),但它会阻止您按名称排序两个Person
对象,然后使用年龄(因为第一个投影的类型为Person -> string
,但第二个的类型为Person -> int
)。
谢谢,非常有帮助 - 内置的比较已经是有用的信息。你在最后提出的问题是:各种返回类型是相关的,至于我必须处理的另一个例子,我有一个浮动和整数的混合包。 – Mathias 2012-04-07 04:31:09
我不认为我正确理解你的问题,但不会下面的代码工作正常吗?
let lowest (points : Point list) = List.sort points |> List.head
看来,F#执行记录数据类型的隐式比较。我的小实验表明,比较恰好是词典。但我找不到任何证据支持这一结果。
所以我不确定F#按字典顺序比较记录。我仍然可以用下面的方式用元组来代替:
let lowest (points : Point list) =
let tuple = List.map (fun pt -> (pt.X, pt.Y)) points |> List.sort |> List.head
{ X = fst tuple; Y = snd tuple }
我希望这篇文章可以帮助。
我的问题不是专门关于点的列表;我所追求的是能够定义要使用的词典顺序。例如,即使上面的排序按X和Y排序,我想要按Y排序,然后X排序 - 我该怎么去做?有趣的是,记录排序虽然默认工作! – Mathias 2012-04-07 19:50:38
可能注意:http://stackoverflow.com/a/211691/19299 – Brian 2012-04-07 07:30:46