Skip to content
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

Return isolated windows from Window* methods #655

Merged
merged 15 commits into from
Nov 20, 2019
Merged

Conversation

Orace
Copy link
Contributor

@Orace Orace commented Nov 6, 2019

This allow tests from #653 to pass and fix #652

@Orace Orace mentioned this pull request Nov 6, 2019
6 tasks
@atifaziz atifaziz changed the title Window* methods should return elements that are isolated from each other. Return isolated windows Window* methods Nov 6, 2019
Orace added 3 commits November 6, 2019 18:40
 - ModifyWindowBeforeMoveNextDoNotAffectNextWindow
 - ModifyWindowAfterMoveNextDoNotAffectNextWindow
 - ModifyWindowBeforeMoveNextDoNotAffectPrevWindow
@Orace
Copy link
Contributor Author

Orace commented Nov 6, 2019

@atifaziz you can review this before #656
Fix #652
Fix #654

@atifaziz atifaziz changed the title Return isolated windows Window* methods Return isolated windows from Window* methods Nov 6, 2019
Copy link
Member

@atifaziz atifaziz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix. The only trouble with the correction is that it now eagerly allocates and prepares the next window, which is wasteful if the iteration never resumes. I like how it's done in F#:

// Copyright (c) Microsoft Corporation.  All Rights Reserved.  See License.txt in the project root for license information.

// windowed : int -> seq<'T> -> seq<'T[]>
[<CompiledName("Windowed")>]
let windowed windowSize (source: seq<_>) =
	checkNonNull "source" source
	if windowSize <= 0 then invalidArgFmt "windowSize" "{0}\nwindowSize = {1}"
								[|SR.GetString SR.inputMustBePositive; windowSize|]
	seq {
		let arr = Array.zeroCreateUnchecked windowSize
		let r = ref (windowSize - 1)
		let i = ref 0
		use e = source.GetEnumerator()
		while e.MoveNext() do
			arr.[!i] <- e.Current
			i := (!i + 1) % windowSize
			if !r = 0 then
				if windowSize < 32 then
					yield Array.init windowSize (fun j -> arr.[(!i+j) % windowSize])
				else
					let result = Array.zeroCreateUnchecked windowSize
					Array.Copy(arr, !i, result, 0, windowSize - !i)
					Array.Copy(arr, 0, result, windowSize - !i, !i)
					yield result
			else r := (!r - 1)
	}

A single working and circular window is maintained internally and it's re-aligned and copied to a new window when it's time to yield. Can we take inspiration from that?

MoreLinq.Test/WindowLeftTest.cs Outdated Show resolved Hide resolved
MoreLinq.Test/WindowLeftTest.cs Outdated Show resolved Hide resolved
MoreLinq.Test/WindowRightTest.cs Outdated Show resolved Hide resolved
MoreLinq.Test/WindowRightTest.cs Outdated Show resolved Hide resolved
MoreLinq.Test/WindowTest.cs Outdated Show resolved Hide resolved
MoreLinq.Test/WindowTest.cs Outdated Show resolved Hide resolved
@Orace
Copy link
Contributor Author

Orace commented Nov 8, 2019

The only trouble with the new correction is that it now eagerly allocates and prepares the next window

this should be fixed in #656 where the only case an array is allocated and not yield is for a call to Window with a sequence shorter than the Window size.

@atifaziz I do a re-read of the code

With current implementation of Window (and of WindowImplem in #656), at each iteration:

  • a new array of size N is allocated.
  • N-1 data are copied from an array to an other

And when the sequence reach its end, all allocated array have been passed to the caller

With your proposed implementation (has I understand it), at each iteration:

  • a new array of size N is allocated.
  • k data are copied from an array to an other
  • N-k data are copied from an array to an other

And when the sequence reach its end, one array has not been passed to the caller

In any case a new array of size N is (and have to, since we give the data to the user) allocated.
In any case (but the last yield), we have to keep the data to be able to build the next Window.

The only "we can do it later" I see is:

yield return window;
newWindow[size - 1] = iter.Current;

instead of

newWindow[size - 1] = iter.Current;
yield return window;

I will put it in #656 ;)
I'm for the current implementation

@Orace Orace requested a review from atifaziz November 8, 2019 18:52
@atifaziz
Copy link
Member

And when the sequence reach its end, all allocated array have been passed to the caller

How do you know you'll reach the end of the sequence. The caller is in control of asking the next window and may stop iteration earlier due to some condition.

Copy link
Member

@atifaziz atifaziz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nitpicks for now.

MoreLinq.Test/WindowLeftTest.cs Outdated Show resolved Hide resolved
MoreLinq.Test/WindowRightTest.cs Outdated Show resolved Hide resolved
MoreLinq.Test/WindowTest.cs Outdated Show resolved Hide resolved
MoreLinq.Test/WindowTest.cs Outdated Show resolved Hide resolved
MoreLinq/Window.cs Outdated Show resolved Hide resolved
@Orace
Copy link
Contributor Author

Orace commented Nov 19, 2019

And when the sequence reach its end, all allocated array have been passed to the caller

How do you know you'll reach the end of the sequence. The caller is in control of asking the next window and may stop iteration earlier due to some condition.

It's an hypothesis.
For a partially enumerated sequence both algorithm instantiate the same number of array (of the size of the window). The number of instantiated array is number of yielded window + 1 in both implementations.
For a fully enumerated sequence your algorithm instantiate one array more than the actual code.

Co-Authored-By: Atif Aziz <code@raboof.com>
@Orace Orace requested a review from atifaziz November 20, 2019 08:48
Copy link
Member

@atifaziz atifaziz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this fix and the tests! 👍

@atifaziz
Copy link
Member

atifaziz commented Nov 20, 2019

@atifaziz I do a re-read of the code

@Orace I re-read as well, and in detail this time, and it looks good! 💯

@atifaziz atifaziz merged commit 50b842d into morelinq:master Nov 20, 2019
@Orace Orace deleted the FixWindow branch November 20, 2019 15:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Windows of Window(Left|Right) are not isolated from each other
2 participants