-
-
Couldn't load subscription status.
- Fork 5.7k
Description
The sort method seems to ignore the ordering when by is defined:
Main> sort!([5,2,7], by = abs, order = Base.Order.Reverse)
3-element Array{Int64,1}:
2
5
7
Main> sort!([5,2,7], by = abs, order = Base.Order.Forward)
3-element Array{Int64,1}:
2
5
7
Version:
Main> versioninfo()
Julia Version 0.7.0-DEV.4690
Commit 78c7d87369* (2018-03-23 22:25 UTC)
Platform Info:
OS: Windows (x86_64-w64-mingw32)
CPU: Intel(R) Core(TM) i5-4440 CPU @ 3.10GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-3.9.1 (ORCJIT, haswell)
Environment:
The reason for this lies in ordering.jl:
abstract type Ordering end
struct ForwardOrdering <: Ordering end
struct ReverseOrdering{Fwd<:Ordering} <: Ordering
fwd::Fwd
end
ReverseOrdering(rev::ReverseOrdering) = rev.fwd
ReverseOrdering(fwd::Fwd) where {Fwd} = ReverseOrdering{Fwd}(fwd)
const DirectOrdering = Union{ForwardOrdering,ReverseOrdering{ForwardOrdering}}
struct By{T} <: Ordering
by::T
end
struct Lt{T} <: Ordering
lt::T
end
lt(o::ForwardOrdering, a, b) = isless(a,b)
lt(o::ReverseOrdering, a, b) = lt(o.fwd,b,a)
lt(o::By, a, b) = isless(o.by(a),o.by(b))
lt(o::Lt, a, b) = o.lt(a,b)
_ord(lt::typeof(isless), by::typeof(identity), order::Ordering) = order
_ord(lt::typeof(isless), by, order::Ordering) = By(by)
_ord(lt, by::typeof(identity), order::Ordering) = Lt(lt)
_ord(lt, by, order::Ordering) = Lt((x,y)->lt(by(x),by(y)))
ord(lt, by, rev::Nothing, order::Ordering=Forward) = _ord(lt, by, order)
function ord(lt, by, rev::Bool, order::Ordering=Forward)
o = _ord(lt, by, order)
return rev ? ReverseOrdering(o) : o
endEssentially the problem is that the specified ordering is not taken into account when lt and by are not default:
_ord(lt::typeof(isless), by::typeof(identity), order::Ordering) = order
_ord(lt::typeof(isless), by, order::Ordering) = By(by)
_ord(lt, by::typeof(identity), order::Ordering) = Lt(lt)
_ord(lt, by, order::Ordering) = Lt((x,y)->lt(by(x),by(y)))In sort.jl, this ordering is then passed down to lt to determine how two elements compare to each other.
If we want to support the ordering option in addition to rev, I think ordering.jl could be implemented in the following manner:
abstract type MyOrdering end
# Some base ordering
struct BinaryOp{T} <: MyOrdering
binary_op::T
end
# Higher order ordering: wraps args in `by(...)`
struct By{O<:MyOrdering,T} <: MyOrdering
by::T
binary_op::O
end
# Higher order ordering: reverses argument of binary operator
struct ReverseOrdering{O<:MyOrdering} <: MyOrdering
binary_op::O
end
# Higher order ordering. A no-op...
struct ForwardOrdering{O<:MyOrdering} <: MyOrdering
binary_op::O
end
(o::BinaryOp)(a, b) = o.binary_op(a, b)
(o::By)(a, b) = o.binary_op(o.by(a), o.by(b))
(o::ReverseOrdering)(a, b) = o.binary_op(b, a)
(o::ForwardOrdering)(a, b) = o.binary_op(a, b)
const DefaultOp = BinaryOp(<)
const MyForward = ForwardOrdering(By(identity,BinaryOp(<)))
const MyReverse = ReverseOrdering(By(identity,BinaryOp(<)))
_ord(lt, by, order::typeof(MyForward)) = ForwardOrdering(By(by,BinaryOp(lt)))
_ord(lt, by, order::typeof(MyReverse)) = ReverseOrdering(By(by,BinaryOp(lt)))
ord(lt, by, rev::Nothing, order::MyOrdering=MyForward) = _ord(lt, by, order)
function ord(lt, by, rev::Bool, order::MyOrdering=MyForward)
o = _ord(lt, by, order)
return rev ? ReverseOrdering(o) : o
endThe functions in sort.jl would need to be modified so that comparison of two elements does not happen with lt but through the MyOrdering instance, for example the function call lt(o, v[i], pivot) would be replaced by o(v[i], pivot) . Some examples:
function example()
@show BinaryOp(<)(-51, 10)
@show By(abs, BinaryOp(<))(-51, 10)
@show Reverse(By(abs, BinaryOp(<)))(-51, 10)
@show Reverse(Reverse(By(abs, BinaryOp(<))))(-51, 10)
end
function example_2()
@show ord(<,identity,false)(1,2)
@show ord(<,abs,false)(1,2)
@show ord(<,abs,true)(1,2)
@show ord(<,abs,true)(1,-2)
@show ord(<,abs,true,MyForward)(1,-2)
@show ord(<,abs,true,MyReverse)(1,-2)
endMain> example()
(BinaryOp(<))(-51, 10) = true
(By(abs, BinaryOp(<)))(-51, 10) = false
(Reverse(By(abs, BinaryOp(<))))(-51, 10) = true
(Reverse(Reverse(By(abs, BinaryOp(<)))))(-51, 10) = false
false
Main> example_2()
(ord(<, identity, false))(1, 2) = true
(ord(<, abs, false))(1, 2) = true
(ord(<, abs, true))(1, 2) = false
(ord(<, abs, true))(1, -2) = false
(ord(<, abs, true, MyForward))(1, -2) = false
(ord(<, abs, true, MyReverse))(1, -2) = true
true
Any ideas? Comments? Incidentally, the current implementation is not type stable and neither is this one.