7. Data.Ord
EDIT. FIXME. Page is too long.
Note: import Data.List
and import Data.Ord
to access these functions.
The Ord
typeclass contains things that can be compared.
An Ordering
is one of three possible results:
LT
(less than) ifx<y
EQ
(equal) ifx==y
GT
(greater than) ifx>y
Some kinds of things that can be compared are: integers, Strings, and tuples.
The compare x y
function takes in two Ord things gives an
Ordering
. Examples:
compare 50 70 == LT
compare "Later" "Earlier" == GT
Tuple comparison is like comparing words in a dictionary. It begins at the first entry and only compares the second entry if the first entries are the same.
compare (4,10) (5,8) == LT
compare (5,10) (5,8) == GT
The normal use of Ord
is in sorting.
Sorting
The Data.List
module has several sorting functions.
Simple sort
-
sort
. Ordinary sorting, from least to greatest.sort [30,50,40,10] == [10,30,40,50] sort ["Butter","Tastes","Awesome"] == ["Awesome","Butter","Tastes"]
Medium: sortOn
-
sortOn
takes in a function of one argument, likeabs
that outputs something that can be ordered.sortOn abs [-10,30,-60,5] == [5,-10,30,-60]
-
The
Down
type creates a new thing that sorts the opposite of the original’s natural direction.Down
is in theData.Ord
module.sortOn Down [1,5,3,6,10] == [10,6,5,3,1]
Danger: the list does not remember the way the items were sorted the first time, so:
step1 = sortOn abs nums
step2 = sortOn Down step1
does not sort in reverse order of absolute value! Instead, it sorts by absolute value and then re-sorts into descending numeric order.
Complex: sortBy
The sortBy
function is the foundation on which all of the other sort
functions are built. It is the most powerful, but also the “lowest
level” and therefore it is usually the least convienient choice.
We are studying it because it provides the foundation on which all of the others build.
The sortBy
function takes in a function of two arguments with a
signature a -> a -> Ordering
and then uses that to order the
list. This function has a purpose like an old fashioned two-pan
balance.
It gives a way to compare the “weights” of two different objects
(the two inputs) and see which one is “heaver” (GT
or LT
).
Here is an example of a silly function that always ranks “Young HS”
above others. Notice how it selects GT
or LT
to make sure that the
“Young HS” string ends up greater than the other one, regardless of
whether that string is in the first or second argument.
cheatCompareWY "Young HS" "Young HS" = EQ
cheatCompareWY "Young HS" _ = GT
cheatCompareWY _ "Young HS" = LT
cheatCompareWY x y = compare x y
wySort strs = sortBy cheatCompareWY strs
Inserting a Down
gives a way to reverse the order of the
sorting. That could go in a helper function, like this:
cheatHelp x y = Down $ cheatCompareWY x y
wySort' strs = sortBy cheatHelp strs
The Down
could also go in a lambda expression, like this:
wySort'' strs = sortBy (\x y -> Down $ cheatCompareWY x y) strs
Example of sortBy
Suppose you want to sort a list of points by distance from the
origin. (This would be easier to do with sortOn
.) You will break
ties by using the ordinary tuple order.
dist (x,y) = x*x+y*y
cmp pt0 pt1
| dist pt0 < dist pt1 = LT
| dist pt0 > dist pt1 = GT
| otherwise = compare pt0 pt1
demox = [(4,1),(-1,4),(1,4),(2,3),(-3,2)]
demoans = sortBy cmp demox
-- [(-3,2),(2,3),(-1,4),(1,4),(4,1)]
Creating the sortBy function (“comparing”)
The comparing
function creates a comparison after applying a
function. The abstract definition of comparing
could be:
comparing f x y = compare (f x) (f y)
The comparing
function immediately creates a function that produces
an ordering.
abscompare = comparing abs
abscompare (-70) 50 == GT -- because |-70| > |50|
Usually this is used to create something suitable to use in
sortBy
. For example:
sortBy (comparing abs) [-70,50,-60,80]
You can actually just define sortOn
using sortBy
and comparing
:
sortOn f xs = sortBy (comparing f) xs
Exercises A: Extended Example
A store has its inventory encoded in a list of tuples.
-- (item name, quantity, cost)
inventory = [("Light Saber", 4, 1000),
("Caltrop", 300, 15),
("Pencil", 5000, 1),
("E-bike", 1, 1200),
("Board Marker", 400, 3)
]
We will define helper functions to get the item_name
,
item_quantity
, and item_cost
.
item_name (n,_,_) = n
item_quantity (_,q,_) = q
item_cost (_,_,c) = c
inventory_value (_,q,c) = q*c
Many of these exercises can be done more than one way. To make sure you learn a variety of methods, some mention a required method.
- (
e1
) Sort the list so the items are in increasing order of quantity. (UsesortBy
.) - (
e2
) Sort the list so the items are in decreasing order of cost. (UsesortOn
.) - (
e3
) Sort the list so that the smallest inventory value (=quantity*value) comes first. - (
e4
) Create a sorting function that will compare inventory value and break ties by using the name of the items (so sort items with the same inventory value alphabetically). Store the sorted list ine4
.
Exercises B
Use these variables in the exercises below.
ex1 :: [Int]
ex1 = [10,-20,30,50,-40,80,-70,-60]
ex2 :: [(String,Int)]
ex2 = [("Anton",300),("Dimitry",130),("Benny",120),("Christian",150)]
ex3 :: [(String,Int)]
ex3 = [("Bar",3),("Bar",90),("Cube",50),("Cube",40)]
- Make the variable
q1
be the listex1
sorted in increasing order. - Make the variable
q2
be the listex1
sorted in decreasing order. - Create
q3
by sorting the listex1
in decreasing order of absolute value (so 80 is the first number in the result) by using negatives. - (
q4
) Repeat the previous exercise, but useDown
instead. - Write a sorting function that works on
(String,Int)
. Suppose you are comparingt1
tow2
. If the String part oft1
comes before “C”, then compare the Int parts oft1
andw2
. Otherwise compare the String parts.