开发者

F# Seq.sortBy in descending order

I am fairly new to F# and came by the Seq.sortBy function however it is sorting my list in ascending order. How do I get it to sort in descending order using the Seq.sort?

For instance an example code would be...

let DisplayList =
seq{0..10}
|> S开发者_开发问答eq.sortBy(fun x -> x)
|> Seq.iter(fun x -> Console.WriteLine(x.ToString()))

gives me an output of 1 2 3 4 5 6 7 8 9 10, when I really want it to do it from 10 to 1.


F# 4.0 (Visual Studio 2015) introduced Seq.sortByDescending and Seq.sortDescending

let DisplayList =
    seq { 0..10 }
    |> Seq.sortDescending         ' or |> Seq.sortByDescending id
    |> Seq.iter Console.WriteLine

See https://github.com/Microsoft/visualfsharp/wiki/F%23-4.0-Status and https://github.com/fsharp/FSharpLangDesign/blob/master/FSharp-4.0/ListSeqArrayAdditions.md


Even shorter:

seq { 0..10 } 
    |> Seq.sortBy (~-)    // Unary minus
    |> Seq.iter (printfn "%d")


Looking at the other answers, beware unary minus and MININT:

let a = [| 1; -1; System.Int32.MinValue; 0; System.Int32.MaxValue; 1 |]

printfn "%A" (a |> Array.sortBy (fun x -> x))
// [|-2147483648; -1; 0; 1; 1; 2147483647|]

printfn "%A" (a |> Array.sortBy (fun x -> -x))  // uh-oh!
// [|-2147483648; 2147483647; 1; 1; 0; -1|]

I think you actually want negative-x-minus-one:

printfn "%A" (a |> Array.sortBy (fun x -> -x - 1))
// [|2147483647; 1; 1; 0; -1; -2147483648|]

for a wraparound integer type that spans -2^N..2^N-1.


First, let's extend Seq with a sortWith function same as List and Array have.

namespace Microsoft.FSharp.Collections
module Seq =
    let sortWith f e = 
        let e' = e |> Seq.toArray
        e' |> Array.sortInPlaceWith f
        e' |> Seq.readonly

Next, let's extend Operators with an often useful flip function.

namespace Microsoft.FSharp.Core
module Operators =
    let flip f x y = f y x

Now, we can leverage the generic compare function for generic (you can use this with any sequence of comparable elements) and safe (in regard to Brian's observation) reverse sequence sort.

{0..10}
|> Seq.sortWith (flip compare)
|> Seq.iter (printfn "%A")


Another option is to wrap System.Linq.Enumerable.OrderByDescending():

// #r "System.Core"
module Seq =
    let sortByDesc f s = Enumerable.OrderByDescending(s, new Func<'a, 'b>(f))

{0..10} |> Seq.sortByDesc (fun x -> x)


You can fix this by providing a negative key

let DisplayList = 
  seq { 0..10 } 
  |> Seq.sortBy (fun x -> -x)
  |> Seq.iter (fun x -> Console.WriteLine(x.ToString()))

Also it's a bit easier (and type safer) to use the printf functions for displaying text in F#. For example

let DisplayList = 
  seq { 0..10 } 
  |> Seq.sortBy (fun x -> -x)
  |> Seq.iter (printfn "%d")


If you know, ahead of time, that you'll have a relatively small sequence, I think this is more readable...

let x = seq { 0.. 10 } |> Seq.toArray |> Array.rev

Of course, its not advisable if you got a potentially very large sequence.


Solutions that use unary minus: (fun x -> -x - 1) and (fun x -> -x) don't work when you have unsigned types:

let a = [| 0uy; 255uy; 254uy; 1uy |]
printfn "%A" (a |> Array.sortBy (fun x -> -x - 1))
// error FS0001: The type 'byte' does not support the operator '~-'

Instead we can use the fact that -x = ~~~x + 1 where ~~~ is a bitwise negation operator and thus -x - 1 = ~~~x. So the short solution that works for both signed and unsigned types:

Array.sortBy (~~~) // equivalent to Array.sortBy (fun x -> ~~~x)

Examples:

let a = [| 0uy; 255uy; 254uy; 1uy |]
printfn "%A" (a |> Array.sortBy (~~~))
// [|255uy; 254uy; 1uy; 0uy|]
let a = [| 1; -1; System.Int32.MinValue; 0; System.Int32.MaxValue; 1 |]
printfn "%A" (a |> Array.sortBy (~~~))
// [|2147483647; 1; 1; 0; -1; -2147483648|]
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜