Test if all elements of a Foldable are the same Announcing the arrival of Valued Associate #679: Cesar Manara Planned maintenance scheduled April 23, 2019 at 23:30 UTC (7:30 pm US/Eastern) Data science time! April 2019 and salary with experience The Ask Question Wizard is Live!An example of a Foldable which is not a Functor (or not Traversable)?Fragile and verbose code using xml-conduitError in function in do notationList processing in HaskellHaskell - Comparing different items with one another within a listComparing a List of tuples in haskellHow to define a parameterized similarity class (an ==-like operator with 3rd param) in Haskell?Foldable, Monoid and MonadExplanation of foldr implementation in FoldableDoes concat return a Foldable?
Marquee sign letters
SQL Server placement of master database files vs resource database files
What's called a person who work as someone who puts products on shelves in stores?
Writing a T-SQL stored procedure to receive 4 numbers and insert them into a table
What to do with someone that cheated their way though university and a PhD program?
Is it accepted to use working hours to read general interest books?
Preserving file and folder permissions with rsync
Simulate round-robin tournament draw
Can gravitational waves pass through a black hole?
Specify the range of GridLines
How do I deal with an erroneously large refund?
Why is arima in R one time step off?
Where to find documentation for `whois` command options?
Are there existing rules/lore for MTG planeswalkers?
When does Bran Stark remember Jamie pushing him?
France's Public Holidays' Puzzle
What is a 'Key' in computer science?
Co-worker works way more than he should
How to begin with a paragraph in latex
Was there ever a LEGO store in Miami International Airport?
Like totally amazing interchangeable sister outfit accessory swapping or whatever
How was Lagrange appointed professor of mathematics so early?
Is it OK if I do not take the receipt in Germany?
What were wait-states, and why was it only an issue for PCs?
Test if all elements of a Foldable are the same
Announcing the arrival of Valued Associate #679: Cesar Manara
Planned maintenance scheduled April 23, 2019 at 23:30 UTC (7:30 pm US/Eastern)
Data science time! April 2019 and salary with experience
The Ask Question Wizard is Live!An example of a Foldable which is not a Functor (or not Traversable)?Fragile and verbose code using xml-conduitError in function in do notationList processing in HaskellHaskell - Comparing different items with one another within a listComparing a List of tuples in haskellHow to define a parameterized similarity class (an ==-like operator with 3rd param) in Haskell?Foldable, Monoid and MonadExplanation of foldr implementation in FoldableDoes concat return a Foldable?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I built a function that verifies that all elements of a foldable structure are equal.
Compared to a similar function on the lists, it seems to me that the more general function is disproportionately complex, but I have not been able to simplify it.
Do you have any suggestions?
import Data.Monoid
import Data.Sequence as SQ
import Data.Matrix as MT
allElementsEqualL :: Eq a => [a] -> Bool
allElementsEqualL [] = True
allElementsEqualL (x:ns) = all (== x) ns
-- allElementsEqualL [1,1,1] -> True
allElementsEqualF :: (Foldable t, Eq a) => t a -> Bool
allElementsEqualF xs = case (getFirst . foldMap (First . Just) $ xs) of
Nothing -> True
Just x -> all (== x) xs
-- allElementsEqualF [1,1,1] -> True
-- allElementsEqualF $ SQ.fromList [1,1,1] -> True
-- allElementsEqualF $ MT.fromLists [[1,1],[1,1]] -> True
haskell
add a comment |
I built a function that verifies that all elements of a foldable structure are equal.
Compared to a similar function on the lists, it seems to me that the more general function is disproportionately complex, but I have not been able to simplify it.
Do you have any suggestions?
import Data.Monoid
import Data.Sequence as SQ
import Data.Matrix as MT
allElementsEqualL :: Eq a => [a] -> Bool
allElementsEqualL [] = True
allElementsEqualL (x:ns) = all (== x) ns
-- allElementsEqualL [1,1,1] -> True
allElementsEqualF :: (Foldable t, Eq a) => t a -> Bool
allElementsEqualF xs = case (getFirst . foldMap (First . Just) $ xs) of
Nothing -> True
Just x -> all (== x) xs
-- allElementsEqualF [1,1,1] -> True
-- allElementsEqualF $ SQ.fromList [1,1,1] -> True
-- allElementsEqualF $ MT.fromLists [[1,1],[1,1]] -> True
haskell
1
Of course, you can always doallElementsEqualF = allElementsEqualL . toList
.
– Alexey Romanov
5 hours ago
@AlexeyRomanov I recently thought of this solution, but I thought it could be very expensive from the point of view of conversion between types. If instead everything happened in a "lazy" way, maybe it would be the most convenient and fastest solution. Is it correct?
– Alberto Capitani
4 hours ago
@AlexeyRomanov I thought also a mixed solution: allElementsEqualF2 xs -- | F.null xs = True -- | otherwise = all (== x) xs -- where -- x = head $ F.toList xs --- so if goList is lazy, the test is carried out upon the original type (with all).
– Alberto Capitani
4 hours ago
I decided it was worth a separate answer after all :)
– Alexey Romanov
2 hours ago
add a comment |
I built a function that verifies that all elements of a foldable structure are equal.
Compared to a similar function on the lists, it seems to me that the more general function is disproportionately complex, but I have not been able to simplify it.
Do you have any suggestions?
import Data.Monoid
import Data.Sequence as SQ
import Data.Matrix as MT
allElementsEqualL :: Eq a => [a] -> Bool
allElementsEqualL [] = True
allElementsEqualL (x:ns) = all (== x) ns
-- allElementsEqualL [1,1,1] -> True
allElementsEqualF :: (Foldable t, Eq a) => t a -> Bool
allElementsEqualF xs = case (getFirst . foldMap (First . Just) $ xs) of
Nothing -> True
Just x -> all (== x) xs
-- allElementsEqualF [1,1,1] -> True
-- allElementsEqualF $ SQ.fromList [1,1,1] -> True
-- allElementsEqualF $ MT.fromLists [[1,1],[1,1]] -> True
haskell
I built a function that verifies that all elements of a foldable structure are equal.
Compared to a similar function on the lists, it seems to me that the more general function is disproportionately complex, but I have not been able to simplify it.
Do you have any suggestions?
import Data.Monoid
import Data.Sequence as SQ
import Data.Matrix as MT
allElementsEqualL :: Eq a => [a] -> Bool
allElementsEqualL [] = True
allElementsEqualL (x:ns) = all (== x) ns
-- allElementsEqualL [1,1,1] -> True
allElementsEqualF :: (Foldable t, Eq a) => t a -> Bool
allElementsEqualF xs = case (getFirst . foldMap (First . Just) $ xs) of
Nothing -> True
Just x -> all (== x) xs
-- allElementsEqualF [1,1,1] -> True
-- allElementsEqualF $ SQ.fromList [1,1,1] -> True
-- allElementsEqualF $ MT.fromLists [[1,1],[1,1]] -> True
haskell
haskell
asked 6 hours ago
Alberto CapitaniAlberto Capitani
534823
534823
1
Of course, you can always doallElementsEqualF = allElementsEqualL . toList
.
– Alexey Romanov
5 hours ago
@AlexeyRomanov I recently thought of this solution, but I thought it could be very expensive from the point of view of conversion between types. If instead everything happened in a "lazy" way, maybe it would be the most convenient and fastest solution. Is it correct?
– Alberto Capitani
4 hours ago
@AlexeyRomanov I thought also a mixed solution: allElementsEqualF2 xs -- | F.null xs = True -- | otherwise = all (== x) xs -- where -- x = head $ F.toList xs --- so if goList is lazy, the test is carried out upon the original type (with all).
– Alberto Capitani
4 hours ago
I decided it was worth a separate answer after all :)
– Alexey Romanov
2 hours ago
add a comment |
1
Of course, you can always doallElementsEqualF = allElementsEqualL . toList
.
– Alexey Romanov
5 hours ago
@AlexeyRomanov I recently thought of this solution, but I thought it could be very expensive from the point of view of conversion between types. If instead everything happened in a "lazy" way, maybe it would be the most convenient and fastest solution. Is it correct?
– Alberto Capitani
4 hours ago
@AlexeyRomanov I thought also a mixed solution: allElementsEqualF2 xs -- | F.null xs = True -- | otherwise = all (== x) xs -- where -- x = head $ F.toList xs --- so if goList is lazy, the test is carried out upon the original type (with all).
– Alberto Capitani
4 hours ago
I decided it was worth a separate answer after all :)
– Alexey Romanov
2 hours ago
1
1
Of course, you can always do
allElementsEqualF = allElementsEqualL . toList
.– Alexey Romanov
5 hours ago
Of course, you can always do
allElementsEqualF = allElementsEqualL . toList
.– Alexey Romanov
5 hours ago
@AlexeyRomanov I recently thought of this solution, but I thought it could be very expensive from the point of view of conversion between types. If instead everything happened in a "lazy" way, maybe it would be the most convenient and fastest solution. Is it correct?
– Alberto Capitani
4 hours ago
@AlexeyRomanov I recently thought of this solution, but I thought it could be very expensive from the point of view of conversion between types. If instead everything happened in a "lazy" way, maybe it would be the most convenient and fastest solution. Is it correct?
– Alberto Capitani
4 hours ago
@AlexeyRomanov I thought also a mixed solution: allElementsEqualF2 xs -- | F.null xs = True -- | otherwise = all (== x) xs -- where -- x = head $ F.toList xs --- so if goList is lazy, the test is carried out upon the original type (with all).
– Alberto Capitani
4 hours ago
@AlexeyRomanov I thought also a mixed solution: allElementsEqualF2 xs -- | F.null xs = True -- | otherwise = all (== x) xs -- where -- x = head $ F.toList xs --- so if goList is lazy, the test is carried out upon the original type (with all).
– Alberto Capitani
4 hours ago
I decided it was worth a separate answer after all :)
– Alexey Romanov
2 hours ago
I decided it was worth a separate answer after all :)
– Alexey Romanov
2 hours ago
add a comment |
4 Answers
4
active
oldest
votes
I don't know about less complicated, but I think this is the "cleanest" way to do it. By "clean," I mean it's one traversal over the structure using a single, special Monoid
.
data Same a = Vacuous | Fail | Same a
instance Eq a => Semigroup (Same a) where
Vacuous <> x = x
Fail <> _ = Fail
s@(Same l) <> Same r = if l == r then s else Fail
x <> Vacuous = x
_ <> Fail = Fail
instance Eq a => Monoid (Same a) where
mempty = Vacuous
allEq :: (Foldable f, Eq a) => f a -> Bool
allEq xs = case foldMap Same xs of
Fail -> False
_ -> True
I thinkSame
is isomorphic toSuccess a
from the zero package.
– Rein Henrichs
6 hours ago
@ReinHenrichs I kind of doubt it.Same a
has two extra constructors compared toa
, whereasSuccess a
has just one. Now it might beSuccess (Maybe a)
or something... but at that point I would say having a custom type is more readable.
– Daniel Wagner
5 hours ago
It is, however, true thatZero (Same a)
withzero = Fail
.
– HTNW
5 hours ago
Same a
hasn + 2
elements.Success a
isMaybe (Maybe a)
, which also hasn + 2
elements. (Ignoring bottoms.)
– Rein Henrichs
5 hours ago
@ReinHenrichs Hm. When I follow your link, I seenewtype Success a = Success getSuccess :: Maybe a
, which has just oneMaybe
wrapper, not two.
– Daniel Wagner
5 hours ago
|
show 2 more comments
The convenient thing about your first function that doesn't exist in your second is that we have a convenient way of getting the "head" of a list. Fortunately, we can do the same for a Foldable
. Let's write a head'
that works on any Foldable
(and for the sake of type safety we'll have our head'
return a Maybe
)
head' :: (Foldable t, Eq a) => t a -> Maybe a
head' = foldr (a _ -> Just a) Nothing
Now we can write basically the same code as the list case for the general one.
allElementsEqualF :: (Foldable t, Eq a) => t a -> Bool
allElementsEqualF f = case head' f of
Nothing -> True
Just a -> all (== a) f
Syntactically, it looks different, but it's the exact same thing you did in your list case: check if the structure is empty and, if it's not, then see if every element is equal to the first.
Note that, technically, this is not quite equivalent to the code you posted, as it compares the first element against itself. So if your ==
operator is for some reason not reflexive, you'll get different results (try running my code and yours on the list [read "NaN" :: Double]
)
add a comment |
Silvio's answer is syntactically small and easy to understand; however, it may do extra work associated with doing two folds if the Foldable
instance can't compute head'
cheaply. In this answer I will discuss how to perform the calculation in just one pass whether the underlying Foldable
can compute head'
cheaply or not.
The basic idea is this: instead of tracking just "are all the elements equal so far", we'll also track what they're all equal to. So:
data AreTheyEqual a
= Empty
| Equal a
| Inequal
deriving Eq
This is a Monoid
, with Empty
as the unit and Inequal
as an absorbing element.
instance Eq a => Semigroup (AreTheyEqual a) where
Empty <> x = x
x <> Empty = x
Equal a <> Equal b | a == b = Equal a
_ <> _ = Inequal
instance Eq a => Monoid (AreTheyEqual a) where
mempty = Empty
Now we can use foldMap
to summarize an entire Foldable
, like so:
allElementsEqual :: (Eq a, Foldable f) => f a -> Bool
allElementsEqual = (Inequal /=) . foldMap Equal
I think this is the same as my answer?
– HTNW
6 hours ago
@HTNW Yep! Seems we overlapped in answering.
– Daniel Wagner
6 hours ago
It doesn't necessarily do two traversals. For structures wherefoldr
can implement guarded recursion, it may only look at the top-most constructor the first time.
– Rein Henrichs
5 hours ago
@ReinHenrichs Good point. I'll amend my answer appropriately.
– Daniel Wagner
5 hours ago
add a comment |
A rather trivial option, and I would generally prefer one of the other answers, is reusing allElementsEqualL
:
allElementsEqualF = allElementsEqualL . toList
or after inlining
allElementsEqualF xs = case toList xs of
[] -> True
x:xs' -> all (== x) xs'
It's laziness which makes it reasonable. The all
call doesn't demand the entire xs'
, but only until it finds the first one different from x
. So toList
will also not demand the entire xs
. And at the same time, already examined elements don't need to be kept in memory.
You could write a Foldable
instance for which toList
is less lazy than necessary, but except for those cases I think it should do exactly as much work as Daniel Wagner's and HTNW's answer (with slight overhead not depending on input size).
I thought also a mixed solution:
allElementsEqualF2 xs | F.null xs = True
| otherwise = all (== x) xs
where x = head $ F.toList xs
so if goList is lazy, the test is carried out upon the original type (with all).
This does slightly more work in the non-empty case than Silvio's answer, because F.null
duplicates exactly as much of F.toList
's work as head'
does. So Silvio's code has to get to the first element 2 times (one for head'
and another inside all
), and yours does it 3 times (null
, head $ toList xs
and all
again).
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55815807%2ftest-if-all-elements-of-a-foldable-are-the-same%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
I don't know about less complicated, but I think this is the "cleanest" way to do it. By "clean," I mean it's one traversal over the structure using a single, special Monoid
.
data Same a = Vacuous | Fail | Same a
instance Eq a => Semigroup (Same a) where
Vacuous <> x = x
Fail <> _ = Fail
s@(Same l) <> Same r = if l == r then s else Fail
x <> Vacuous = x
_ <> Fail = Fail
instance Eq a => Monoid (Same a) where
mempty = Vacuous
allEq :: (Foldable f, Eq a) => f a -> Bool
allEq xs = case foldMap Same xs of
Fail -> False
_ -> True
I thinkSame
is isomorphic toSuccess a
from the zero package.
– Rein Henrichs
6 hours ago
@ReinHenrichs I kind of doubt it.Same a
has two extra constructors compared toa
, whereasSuccess a
has just one. Now it might beSuccess (Maybe a)
or something... but at that point I would say having a custom type is more readable.
– Daniel Wagner
5 hours ago
It is, however, true thatZero (Same a)
withzero = Fail
.
– HTNW
5 hours ago
Same a
hasn + 2
elements.Success a
isMaybe (Maybe a)
, which also hasn + 2
elements. (Ignoring bottoms.)
– Rein Henrichs
5 hours ago
@ReinHenrichs Hm. When I follow your link, I seenewtype Success a = Success getSuccess :: Maybe a
, which has just oneMaybe
wrapper, not two.
– Daniel Wagner
5 hours ago
|
show 2 more comments
I don't know about less complicated, but I think this is the "cleanest" way to do it. By "clean," I mean it's one traversal over the structure using a single, special Monoid
.
data Same a = Vacuous | Fail | Same a
instance Eq a => Semigroup (Same a) where
Vacuous <> x = x
Fail <> _ = Fail
s@(Same l) <> Same r = if l == r then s else Fail
x <> Vacuous = x
_ <> Fail = Fail
instance Eq a => Monoid (Same a) where
mempty = Vacuous
allEq :: (Foldable f, Eq a) => f a -> Bool
allEq xs = case foldMap Same xs of
Fail -> False
_ -> True
I thinkSame
is isomorphic toSuccess a
from the zero package.
– Rein Henrichs
6 hours ago
@ReinHenrichs I kind of doubt it.Same a
has two extra constructors compared toa
, whereasSuccess a
has just one. Now it might beSuccess (Maybe a)
or something... but at that point I would say having a custom type is more readable.
– Daniel Wagner
5 hours ago
It is, however, true thatZero (Same a)
withzero = Fail
.
– HTNW
5 hours ago
Same a
hasn + 2
elements.Success a
isMaybe (Maybe a)
, which also hasn + 2
elements. (Ignoring bottoms.)
– Rein Henrichs
5 hours ago
@ReinHenrichs Hm. When I follow your link, I seenewtype Success a = Success getSuccess :: Maybe a
, which has just oneMaybe
wrapper, not two.
– Daniel Wagner
5 hours ago
|
show 2 more comments
I don't know about less complicated, but I think this is the "cleanest" way to do it. By "clean," I mean it's one traversal over the structure using a single, special Monoid
.
data Same a = Vacuous | Fail | Same a
instance Eq a => Semigroup (Same a) where
Vacuous <> x = x
Fail <> _ = Fail
s@(Same l) <> Same r = if l == r then s else Fail
x <> Vacuous = x
_ <> Fail = Fail
instance Eq a => Monoid (Same a) where
mempty = Vacuous
allEq :: (Foldable f, Eq a) => f a -> Bool
allEq xs = case foldMap Same xs of
Fail -> False
_ -> True
I don't know about less complicated, but I think this is the "cleanest" way to do it. By "clean," I mean it's one traversal over the structure using a single, special Monoid
.
data Same a = Vacuous | Fail | Same a
instance Eq a => Semigroup (Same a) where
Vacuous <> x = x
Fail <> _ = Fail
s@(Same l) <> Same r = if l == r then s else Fail
x <> Vacuous = x
_ <> Fail = Fail
instance Eq a => Monoid (Same a) where
mempty = Vacuous
allEq :: (Foldable f, Eq a) => f a -> Bool
allEq xs = case foldMap Same xs of
Fail -> False
_ -> True
edited 20 mins ago
Joseph Sible
7,52031337
7,52031337
answered 6 hours ago
HTNWHTNW
10.5k1832
10.5k1832
I thinkSame
is isomorphic toSuccess a
from the zero package.
– Rein Henrichs
6 hours ago
@ReinHenrichs I kind of doubt it.Same a
has two extra constructors compared toa
, whereasSuccess a
has just one. Now it might beSuccess (Maybe a)
or something... but at that point I would say having a custom type is more readable.
– Daniel Wagner
5 hours ago
It is, however, true thatZero (Same a)
withzero = Fail
.
– HTNW
5 hours ago
Same a
hasn + 2
elements.Success a
isMaybe (Maybe a)
, which also hasn + 2
elements. (Ignoring bottoms.)
– Rein Henrichs
5 hours ago
@ReinHenrichs Hm. When I follow your link, I seenewtype Success a = Success getSuccess :: Maybe a
, which has just oneMaybe
wrapper, not two.
– Daniel Wagner
5 hours ago
|
show 2 more comments
I thinkSame
is isomorphic toSuccess a
from the zero package.
– Rein Henrichs
6 hours ago
@ReinHenrichs I kind of doubt it.Same a
has two extra constructors compared toa
, whereasSuccess a
has just one. Now it might beSuccess (Maybe a)
or something... but at that point I would say having a custom type is more readable.
– Daniel Wagner
5 hours ago
It is, however, true thatZero (Same a)
withzero = Fail
.
– HTNW
5 hours ago
Same a
hasn + 2
elements.Success a
isMaybe (Maybe a)
, which also hasn + 2
elements. (Ignoring bottoms.)
– Rein Henrichs
5 hours ago
@ReinHenrichs Hm. When I follow your link, I seenewtype Success a = Success getSuccess :: Maybe a
, which has just oneMaybe
wrapper, not two.
– Daniel Wagner
5 hours ago
I think
Same
is isomorphic to Success a
from the zero package.– Rein Henrichs
6 hours ago
I think
Same
is isomorphic to Success a
from the zero package.– Rein Henrichs
6 hours ago
@ReinHenrichs I kind of doubt it.
Same a
has two extra constructors compared to a
, whereas Success a
has just one. Now it might be Success (Maybe a)
or something... but at that point I would say having a custom type is more readable.– Daniel Wagner
5 hours ago
@ReinHenrichs I kind of doubt it.
Same a
has two extra constructors compared to a
, whereas Success a
has just one. Now it might be Success (Maybe a)
or something... but at that point I would say having a custom type is more readable.– Daniel Wagner
5 hours ago
It is, however, true that
Zero (Same a)
with zero = Fail
.– HTNW
5 hours ago
It is, however, true that
Zero (Same a)
with zero = Fail
.– HTNW
5 hours ago
Same a
has n + 2
elements. Success a
is Maybe (Maybe a)
, which also has n + 2
elements. (Ignoring bottoms.)– Rein Henrichs
5 hours ago
Same a
has n + 2
elements. Success a
is Maybe (Maybe a)
, which also has n + 2
elements. (Ignoring bottoms.)– Rein Henrichs
5 hours ago
@ReinHenrichs Hm. When I follow your link, I see
newtype Success a = Success getSuccess :: Maybe a
, which has just one Maybe
wrapper, not two.– Daniel Wagner
5 hours ago
@ReinHenrichs Hm. When I follow your link, I see
newtype Success a = Success getSuccess :: Maybe a
, which has just one Maybe
wrapper, not two.– Daniel Wagner
5 hours ago
|
show 2 more comments
The convenient thing about your first function that doesn't exist in your second is that we have a convenient way of getting the "head" of a list. Fortunately, we can do the same for a Foldable
. Let's write a head'
that works on any Foldable
(and for the sake of type safety we'll have our head'
return a Maybe
)
head' :: (Foldable t, Eq a) => t a -> Maybe a
head' = foldr (a _ -> Just a) Nothing
Now we can write basically the same code as the list case for the general one.
allElementsEqualF :: (Foldable t, Eq a) => t a -> Bool
allElementsEqualF f = case head' f of
Nothing -> True
Just a -> all (== a) f
Syntactically, it looks different, but it's the exact same thing you did in your list case: check if the structure is empty and, if it's not, then see if every element is equal to the first.
Note that, technically, this is not quite equivalent to the code you posted, as it compares the first element against itself. So if your ==
operator is for some reason not reflexive, you'll get different results (try running my code and yours on the list [read "NaN" :: Double]
)
add a comment |
The convenient thing about your first function that doesn't exist in your second is that we have a convenient way of getting the "head" of a list. Fortunately, we can do the same for a Foldable
. Let's write a head'
that works on any Foldable
(and for the sake of type safety we'll have our head'
return a Maybe
)
head' :: (Foldable t, Eq a) => t a -> Maybe a
head' = foldr (a _ -> Just a) Nothing
Now we can write basically the same code as the list case for the general one.
allElementsEqualF :: (Foldable t, Eq a) => t a -> Bool
allElementsEqualF f = case head' f of
Nothing -> True
Just a -> all (== a) f
Syntactically, it looks different, but it's the exact same thing you did in your list case: check if the structure is empty and, if it's not, then see if every element is equal to the first.
Note that, technically, this is not quite equivalent to the code you posted, as it compares the first element against itself. So if your ==
operator is for some reason not reflexive, you'll get different results (try running my code and yours on the list [read "NaN" :: Double]
)
add a comment |
The convenient thing about your first function that doesn't exist in your second is that we have a convenient way of getting the "head" of a list. Fortunately, we can do the same for a Foldable
. Let's write a head'
that works on any Foldable
(and for the sake of type safety we'll have our head'
return a Maybe
)
head' :: (Foldable t, Eq a) => t a -> Maybe a
head' = foldr (a _ -> Just a) Nothing
Now we can write basically the same code as the list case for the general one.
allElementsEqualF :: (Foldable t, Eq a) => t a -> Bool
allElementsEqualF f = case head' f of
Nothing -> True
Just a -> all (== a) f
Syntactically, it looks different, but it's the exact same thing you did in your list case: check if the structure is empty and, if it's not, then see if every element is equal to the first.
Note that, technically, this is not quite equivalent to the code you posted, as it compares the first element against itself. So if your ==
operator is for some reason not reflexive, you'll get different results (try running my code and yours on the list [read "NaN" :: Double]
)
The convenient thing about your first function that doesn't exist in your second is that we have a convenient way of getting the "head" of a list. Fortunately, we can do the same for a Foldable
. Let's write a head'
that works on any Foldable
(and for the sake of type safety we'll have our head'
return a Maybe
)
head' :: (Foldable t, Eq a) => t a -> Maybe a
head' = foldr (a _ -> Just a) Nothing
Now we can write basically the same code as the list case for the general one.
allElementsEqualF :: (Foldable t, Eq a) => t a -> Bool
allElementsEqualF f = case head' f of
Nothing -> True
Just a -> all (== a) f
Syntactically, it looks different, but it's the exact same thing you did in your list case: check if the structure is empty and, if it's not, then see if every element is equal to the first.
Note that, technically, this is not quite equivalent to the code you posted, as it compares the first element against itself. So if your ==
operator is for some reason not reflexive, you'll get different results (try running my code and yours on the list [read "NaN" :: Double]
)
answered 6 hours ago
Silvio MayoloSilvio Mayolo
14.9k22654
14.9k22654
add a comment |
add a comment |
Silvio's answer is syntactically small and easy to understand; however, it may do extra work associated with doing two folds if the Foldable
instance can't compute head'
cheaply. In this answer I will discuss how to perform the calculation in just one pass whether the underlying Foldable
can compute head'
cheaply or not.
The basic idea is this: instead of tracking just "are all the elements equal so far", we'll also track what they're all equal to. So:
data AreTheyEqual a
= Empty
| Equal a
| Inequal
deriving Eq
This is a Monoid
, with Empty
as the unit and Inequal
as an absorbing element.
instance Eq a => Semigroup (AreTheyEqual a) where
Empty <> x = x
x <> Empty = x
Equal a <> Equal b | a == b = Equal a
_ <> _ = Inequal
instance Eq a => Monoid (AreTheyEqual a) where
mempty = Empty
Now we can use foldMap
to summarize an entire Foldable
, like so:
allElementsEqual :: (Eq a, Foldable f) => f a -> Bool
allElementsEqual = (Inequal /=) . foldMap Equal
I think this is the same as my answer?
– HTNW
6 hours ago
@HTNW Yep! Seems we overlapped in answering.
– Daniel Wagner
6 hours ago
It doesn't necessarily do two traversals. For structures wherefoldr
can implement guarded recursion, it may only look at the top-most constructor the first time.
– Rein Henrichs
5 hours ago
@ReinHenrichs Good point. I'll amend my answer appropriately.
– Daniel Wagner
5 hours ago
add a comment |
Silvio's answer is syntactically small and easy to understand; however, it may do extra work associated with doing two folds if the Foldable
instance can't compute head'
cheaply. In this answer I will discuss how to perform the calculation in just one pass whether the underlying Foldable
can compute head'
cheaply or not.
The basic idea is this: instead of tracking just "are all the elements equal so far", we'll also track what they're all equal to. So:
data AreTheyEqual a
= Empty
| Equal a
| Inequal
deriving Eq
This is a Monoid
, with Empty
as the unit and Inequal
as an absorbing element.
instance Eq a => Semigroup (AreTheyEqual a) where
Empty <> x = x
x <> Empty = x
Equal a <> Equal b | a == b = Equal a
_ <> _ = Inequal
instance Eq a => Monoid (AreTheyEqual a) where
mempty = Empty
Now we can use foldMap
to summarize an entire Foldable
, like so:
allElementsEqual :: (Eq a, Foldable f) => f a -> Bool
allElementsEqual = (Inequal /=) . foldMap Equal
I think this is the same as my answer?
– HTNW
6 hours ago
@HTNW Yep! Seems we overlapped in answering.
– Daniel Wagner
6 hours ago
It doesn't necessarily do two traversals. For structures wherefoldr
can implement guarded recursion, it may only look at the top-most constructor the first time.
– Rein Henrichs
5 hours ago
@ReinHenrichs Good point. I'll amend my answer appropriately.
– Daniel Wagner
5 hours ago
add a comment |
Silvio's answer is syntactically small and easy to understand; however, it may do extra work associated with doing two folds if the Foldable
instance can't compute head'
cheaply. In this answer I will discuss how to perform the calculation in just one pass whether the underlying Foldable
can compute head'
cheaply or not.
The basic idea is this: instead of tracking just "are all the elements equal so far", we'll also track what they're all equal to. So:
data AreTheyEqual a
= Empty
| Equal a
| Inequal
deriving Eq
This is a Monoid
, with Empty
as the unit and Inequal
as an absorbing element.
instance Eq a => Semigroup (AreTheyEqual a) where
Empty <> x = x
x <> Empty = x
Equal a <> Equal b | a == b = Equal a
_ <> _ = Inequal
instance Eq a => Monoid (AreTheyEqual a) where
mempty = Empty
Now we can use foldMap
to summarize an entire Foldable
, like so:
allElementsEqual :: (Eq a, Foldable f) => f a -> Bool
allElementsEqual = (Inequal /=) . foldMap Equal
Silvio's answer is syntactically small and easy to understand; however, it may do extra work associated with doing two folds if the Foldable
instance can't compute head'
cheaply. In this answer I will discuss how to perform the calculation in just one pass whether the underlying Foldable
can compute head'
cheaply or not.
The basic idea is this: instead of tracking just "are all the elements equal so far", we'll also track what they're all equal to. So:
data AreTheyEqual a
= Empty
| Equal a
| Inequal
deriving Eq
This is a Monoid
, with Empty
as the unit and Inequal
as an absorbing element.
instance Eq a => Semigroup (AreTheyEqual a) where
Empty <> x = x
x <> Empty = x
Equal a <> Equal b | a == b = Equal a
_ <> _ = Inequal
instance Eq a => Monoid (AreTheyEqual a) where
mempty = Empty
Now we can use foldMap
to summarize an entire Foldable
, like so:
allElementsEqual :: (Eq a, Foldable f) => f a -> Bool
allElementsEqual = (Inequal /=) . foldMap Equal
edited 5 hours ago
answered 6 hours ago
Daniel WagnerDaniel Wagner
104k7163288
104k7163288
I think this is the same as my answer?
– HTNW
6 hours ago
@HTNW Yep! Seems we overlapped in answering.
– Daniel Wagner
6 hours ago
It doesn't necessarily do two traversals. For structures wherefoldr
can implement guarded recursion, it may only look at the top-most constructor the first time.
– Rein Henrichs
5 hours ago
@ReinHenrichs Good point. I'll amend my answer appropriately.
– Daniel Wagner
5 hours ago
add a comment |
I think this is the same as my answer?
– HTNW
6 hours ago
@HTNW Yep! Seems we overlapped in answering.
– Daniel Wagner
6 hours ago
It doesn't necessarily do two traversals. For structures wherefoldr
can implement guarded recursion, it may only look at the top-most constructor the first time.
– Rein Henrichs
5 hours ago
@ReinHenrichs Good point. I'll amend my answer appropriately.
– Daniel Wagner
5 hours ago
I think this is the same as my answer?
– HTNW
6 hours ago
I think this is the same as my answer?
– HTNW
6 hours ago
@HTNW Yep! Seems we overlapped in answering.
– Daniel Wagner
6 hours ago
@HTNW Yep! Seems we overlapped in answering.
– Daniel Wagner
6 hours ago
It doesn't necessarily do two traversals. For structures where
foldr
can implement guarded recursion, it may only look at the top-most constructor the first time.– Rein Henrichs
5 hours ago
It doesn't necessarily do two traversals. For structures where
foldr
can implement guarded recursion, it may only look at the top-most constructor the first time.– Rein Henrichs
5 hours ago
@ReinHenrichs Good point. I'll amend my answer appropriately.
– Daniel Wagner
5 hours ago
@ReinHenrichs Good point. I'll amend my answer appropriately.
– Daniel Wagner
5 hours ago
add a comment |
A rather trivial option, and I would generally prefer one of the other answers, is reusing allElementsEqualL
:
allElementsEqualF = allElementsEqualL . toList
or after inlining
allElementsEqualF xs = case toList xs of
[] -> True
x:xs' -> all (== x) xs'
It's laziness which makes it reasonable. The all
call doesn't demand the entire xs'
, but only until it finds the first one different from x
. So toList
will also not demand the entire xs
. And at the same time, already examined elements don't need to be kept in memory.
You could write a Foldable
instance for which toList
is less lazy than necessary, but except for those cases I think it should do exactly as much work as Daniel Wagner's and HTNW's answer (with slight overhead not depending on input size).
I thought also a mixed solution:
allElementsEqualF2 xs | F.null xs = True
| otherwise = all (== x) xs
where x = head $ F.toList xs
so if goList is lazy, the test is carried out upon the original type (with all).
This does slightly more work in the non-empty case than Silvio's answer, because F.null
duplicates exactly as much of F.toList
's work as head'
does. So Silvio's code has to get to the first element 2 times (one for head'
and another inside all
), and yours does it 3 times (null
, head $ toList xs
and all
again).
add a comment |
A rather trivial option, and I would generally prefer one of the other answers, is reusing allElementsEqualL
:
allElementsEqualF = allElementsEqualL . toList
or after inlining
allElementsEqualF xs = case toList xs of
[] -> True
x:xs' -> all (== x) xs'
It's laziness which makes it reasonable. The all
call doesn't demand the entire xs'
, but only until it finds the first one different from x
. So toList
will also not demand the entire xs
. And at the same time, already examined elements don't need to be kept in memory.
You could write a Foldable
instance for which toList
is less lazy than necessary, but except for those cases I think it should do exactly as much work as Daniel Wagner's and HTNW's answer (with slight overhead not depending on input size).
I thought also a mixed solution:
allElementsEqualF2 xs | F.null xs = True
| otherwise = all (== x) xs
where x = head $ F.toList xs
so if goList is lazy, the test is carried out upon the original type (with all).
This does slightly more work in the non-empty case than Silvio's answer, because F.null
duplicates exactly as much of F.toList
's work as head'
does. So Silvio's code has to get to the first element 2 times (one for head'
and another inside all
), and yours does it 3 times (null
, head $ toList xs
and all
again).
add a comment |
A rather trivial option, and I would generally prefer one of the other answers, is reusing allElementsEqualL
:
allElementsEqualF = allElementsEqualL . toList
or after inlining
allElementsEqualF xs = case toList xs of
[] -> True
x:xs' -> all (== x) xs'
It's laziness which makes it reasonable. The all
call doesn't demand the entire xs'
, but only until it finds the first one different from x
. So toList
will also not demand the entire xs
. And at the same time, already examined elements don't need to be kept in memory.
You could write a Foldable
instance for which toList
is less lazy than necessary, but except for those cases I think it should do exactly as much work as Daniel Wagner's and HTNW's answer (with slight overhead not depending on input size).
I thought also a mixed solution:
allElementsEqualF2 xs | F.null xs = True
| otherwise = all (== x) xs
where x = head $ F.toList xs
so if goList is lazy, the test is carried out upon the original type (with all).
This does slightly more work in the non-empty case than Silvio's answer, because F.null
duplicates exactly as much of F.toList
's work as head'
does. So Silvio's code has to get to the first element 2 times (one for head'
and another inside all
), and yours does it 3 times (null
, head $ toList xs
and all
again).
A rather trivial option, and I would generally prefer one of the other answers, is reusing allElementsEqualL
:
allElementsEqualF = allElementsEqualL . toList
or after inlining
allElementsEqualF xs = case toList xs of
[] -> True
x:xs' -> all (== x) xs'
It's laziness which makes it reasonable. The all
call doesn't demand the entire xs'
, but only until it finds the first one different from x
. So toList
will also not demand the entire xs
. And at the same time, already examined elements don't need to be kept in memory.
You could write a Foldable
instance for which toList
is less lazy than necessary, but except for those cases I think it should do exactly as much work as Daniel Wagner's and HTNW's answer (with slight overhead not depending on input size).
I thought also a mixed solution:
allElementsEqualF2 xs | F.null xs = True
| otherwise = all (== x) xs
where x = head $ F.toList xs
so if goList is lazy, the test is carried out upon the original type (with all).
This does slightly more work in the non-empty case than Silvio's answer, because F.null
duplicates exactly as much of F.toList
's work as head'
does. So Silvio's code has to get to the first element 2 times (one for head'
and another inside all
), and yours does it 3 times (null
, head $ toList xs
and all
again).
edited 1 hour ago
answered 2 hours ago
Alexey RomanovAlexey Romanov
112k26217360
112k26217360
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55815807%2ftest-if-all-elements-of-a-foldable-are-the-same%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Of course, you can always do
allElementsEqualF = allElementsEqualL . toList
.– Alexey Romanov
5 hours ago
@AlexeyRomanov I recently thought of this solution, but I thought it could be very expensive from the point of view of conversion between types. If instead everything happened in a "lazy" way, maybe it would be the most convenient and fastest solution. Is it correct?
– Alberto Capitani
4 hours ago
@AlexeyRomanov I thought also a mixed solution: allElementsEqualF2 xs -- | F.null xs = True -- | otherwise = all (== x) xs -- where -- x = head $ F.toList xs --- so if goList is lazy, the test is carried out upon the original type (with all).
– Alberto Capitani
4 hours ago
I decided it was worth a separate answer after all :)
– Alexey Romanov
2 hours ago