-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve the time performance of Data.List.unsnoc
#307
Comments
Dropping the tilde changes the behavior, namely, head . fst <$> unsnoc (1 : 2 : undefined) will diverge. See the original discussion of implementation in #165 (comment) I'm supportive of changing the definition of
(In the context of #292, it is plausible that we might want to add |
@Bodigrim, is that a green light to create a merge request consistent with the |
From my personal perspective - yes, although I’d love to hear from others. But you can fire up an MR - maybe it will help to gather more feedback. |
I have created: |
I commented on the MR, but overall looks good to me. Dear CLC members, any more opinions before we vote? |
This difference is likely because The version here is different from the benchmarked function above and appropriately lazy.
Not surprisingly, it is worse when there is fusion. -- a simple case where we expect fusion is [1..n]
, bgroup "unsnoc fusion" $
let n = 10000 :: Int in
[ bench "unsnoc" $ nf (\i -> unsnoc [1..i]) n
, bench "unsnocGo" $ nf (\i -> unsnocGo [1..i]) n
]
So I don't think this change would be an improvement. |
I doubt it's possible to make
If anything, the benchmarks in the OP indicate that using |
I can reproduce @meooow25's observation that adding the tilde to
If anything, |
This issue may be misconceived (sorry). If: >>> head . fst <$> unsnoc (1 : 2 : undefined)
Just 1 is a requirement,§ then perhaps the (slow) existing § EDIT: The GHC project's version ( -- | Split a list into its last element and the initial part of the list.
-- @snocView xs = Just (init xs, last xs)@ for non-empty lists.
-- @snocView xs = Nothing@ otherwise.
-- Unless both parts of the result are guaranteed to be used
-- prefer separate calls to @last@ + @init@.
-- If you are guaranteed to use both, this will
-- be more efficient.
snocView :: [a] -> Maybe ([a],a)
snocView = fmap go . nonEmpty
where
go :: NonEmpty a -> ([a],a)
go (x:|xs) = case nonEmpty xs of
Nothing -> ([],x)
Just xs -> case go xs of !(xs', x') -> (x:xs', x') |
I think for the purposes of #292 it might be more fruitful to focus on pushing for separate |
Motivation:
Specifically, @meooow25 observed:
It seemed that 'naive':
had similar time performance to partial:
while existing
Data.List.unsnoc
was much slower than both.@Bodigrim observed:
The following experiment (with GHC 9.8.3) using the
criterion
package:had results that were typically like this:
That is, the 'naive' total version is about as quick as a 'partial' version. The copy of
base
unsnoc
is well over three times slower. Dropping the tilde saves about 15% in time. Finally, the 'go' version is about 1.4 times slower than the 'naive' total version.The text was updated successfully, but these errors were encountered: