Comparison chain: Difference between revisions
(add another way) |
Hairy Dude (talk | contribs) m (→Solutions: copy editing) |
||
(One intermediate revision by the same user not shown) | |||
Line 5: | Line 5: | ||
Answer: | Answer: | ||
The expression cannot be parsed, | The expression cannot be parsed, because the infix symbol <hask> <= </hask> has no (left or right) associativity. | ||
because the infix symbol <hask> <= </hask> has no (left or right) associativity. | |||
In languages like C | In languages like C | ||
Line 32: | Line 31: | ||
</haskell> | </haskell> | ||
:(<hask>x <? (a,b)</hask>). In case of integers you can use the <hask>inRange</hask> function from <hask>Ix</hask> class. | :(<hask>x <? (a,b)</hask>). In case of integers you can use the <hask>inRange</hask> function from <hask>Ix</hask> class. | ||
* You can easily write a function, which checks if a list of numbers increases | * You can easily write a function, which checks if a list of numbers increases monotonically. | ||
: | : | ||
<haskell> | <haskell> |
Latest revision as of 23:08, 26 January 2016
Problem
Question:
The compiler doesn't accept a <= x <= b
. Why?
Answer:
The expression cannot be parsed, because the infix symbol <=
has no (left or right) associativity.
In languages like C
the expression is parsed as (a <= x) <= b
which is even worse.
The first part is evaluated to a boolean value,
which is then compared with b
.
(For C "boolean" and "integer" are the same type.)
Solutions
simple
- You must be aware, that the mathematical notation is shorthand for . Consequently a possible Haskell solution is
a <= x && x <= b
. - Another fine mathematical notation is . You can roll your own function
isInRange :: Ord a => a -> a -> a -> Bool
isInRange lower upper x = lower <= x && x <= upper
- to capture this notation (
isInRange a b x
), or
(<?) :: Ord a => a -> (a,a) -> Bool
(<?) = flip (uncurry isInRange)
- (
x <? (a,b)
). In case of integers you can use theinRange
function fromIx
class.
- You can easily write a function, which checks if a list of numbers increases monotonically.
monotonicIncreasing :: Ord a => [a] -> Bool
monotonicIncreasing xs = and (zipWith (<=) xs (tail xs))
- You can use that for the initial problem by
monotonicIncreasing [a,x,b]
.
complex
- For more complex checks of whether an element is contained in some ranges, you should have a look at the ranged sets library.
- If you want to program more complex chains with different kinds of comparisons, try the following code.
module ChainRelation where
{- * chains of relations (comparison, subsets, logical implications etc.) -}
infixr 4 &-, -&
type Rel a = (a -> a -> Bool)
type Chain a = [(Rel a, a)]
endChain :: Chain a
endChain = []
-- separate comparison and operand
(&-) :: Rel a -> (a, Chain a) -> Chain a
rel &- (x,xs) = (rel,x):xs
-- separate operand and comparison
(-&) :: a -> Chain a -> (a, Chain a)
(-&) = (,)
-- check if all comparisons are true
check :: (a, Chain a) -> Bool
check (x,chain) =
let (rels,xs) = unzip chain
in and (zipWith3 id rels (x:xs) xs)
example1 :: Bool
example1 =
check (1 -& (<) &- 5 -& (==) &- 5 -& (<=) &- 10 -&
(endChain :: Chain Integer))
{- * specialised infix operators for comparison -}
infixr 4 ==:, /=:, <:, >:, <=:, >=:
(==:), (/=:), (<:), (>:), (<=:), (>=:) :: Ord a =>
a -> (a, Chain a) -> (a, Chain a)
(==:) = lift (==)
(/=:) = lift (/=)
(<:) = lift (<)
(>:) = lift (>)
(<=:) = lift (<=)
(>=:) = lift (>=)
lift :: Rel a -> a -> (a, Chain a) -> (a, Chain a)
lift f x (y,chain) = (x, (f,y):chain)
example2 :: Bool
example2 =
check (1 <: 5 ==: 5 <=: 10
-& (endChain :: Chain Integer))
- You can represent a successful sequence of comparisons with
Just a
and a failed sequence withNothing
:
module ChainRelation2 where
import Data.Maybe (isJust)
lift :: (a -> b -> Bool) -> Maybe a -> b -> Maybe b
lift f (Just x) y | f x y = Just y
lift _ _ _ = Nothing
(<:) = lift (<)
(<=:) = lift (<=)
(*:) = lift elem
example = isJust (Just 4 <: 5 <=: 6 *: [6,7])
-- Python equivalent: 4 < 5 <= 6 in [6,7]