-
Notifications
You must be signed in to change notification settings - Fork 21
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
Allow negative indices in indexing and slicing like python #358
Comments
I wanted this a couple of times in the last few months. Was surprised when I discovered that this wasn't supported. |
See also the good discussion on #518 |
It's not always true that the compiler "could just do that conversion behind the scenes", if the value that could be negative is in a variable. Code like The question is whether My gut feeling is that |
This syntax is terrible.
This suggestion is to take this strange behavior, and see an opportunity to hack it to make negative numbers do something completely different from weakly positive numbers and 0..y slicing completely different from ..y slicing. If l = [0;1;2] then l.[..2] = [0;1;2], l[..1] = [0;1], l.[..0]=[0], and according to this suggestion l.[-1]=[0;1]. That doesn't make any sense. Also l.[.. -2] would "mean" l.[.. 1-2]=l.[.. -1], which would "mean" l.[..1]. Modulo arithmetic doesn't match up well with inequalities. Having a shorthand to avoid a.Length could be useful, and similarly having simpler reverse slicing could be useful, but the syntax should not overlap inconsistently with existing slicing. |
My mind tried this syntax to get last three characters:
and this would get third of last, and second of last:
This would match the substr e.g. in PHP:
|
@rmunn Perhaps the compiler could could add a branch in there, and compile let i = if x > 0 then x else a.Length - x
a.[0..i] |
Slicing was recently made into something logical with the implementation of consistent slicing rfc. Please see the current definition of the slicing operations there. We should not descend to the level of PHP. By all means we can define define |
The new Index type would be the right way to implement this. |
The concept of negative indices; i.e., "from the end", is something we should support. There are a few things to consider: SyntaxThe Range type for C#/.NET will use I couldn't find other examples in other languages. Using Using > let ( .^ ) x y = x + y;;
val ( .^ ) : x:int -> y:int -> int Given this and the ubiquity of Python I would support Allow in the startPython also supports negative indices as the first value. Its meaning is no different; it simply means "from the start": >>> lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> lst[-5:-4]
[6] The example given is a bit silly, but it is consistent. Once the end index becomes more negative than the start index, the result is an empty list. I think it's perfectly fine to allow a negative index in the start. Edge casesNeither Python nor C#/.NET will support indices that are out of bounds of the collection. It should be a fairly common sense rule that giving an index of -50 to a list of size 3 results in an exception. This should be the same in F#. In Python, if the end index is more negative than the start index (when both are negative), the result is an empty list. It could also make sense for that to be an error. Both Python and C#/.NET have the upper bound as exclusive. F# upper bounds are inclusive, which means that code in Python or C#/.NET is not directly translatable. |
I vote for
|
>>> lst
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> lst[0:-1]
[1, 2, 3, 4, 5, 6, 7, 8, 9] The question of syntax isn't necessarily driven by consistency. IIRC C# had to pick |
It's also the case that redefining I like C#'s approach better then Python's because in Python you specify which definition you want to use inside the data, and in C# you choose which definition you want to use explicitly. |
I don't understand what "logical reasons" means. Regardless of syntax, Another tricky thing to consider is that if we were to adopt the same syntax as C# with
While true, we do have a precedent for introducing breaking changes like this (hell, even source breaking changes) so long as they are communicated up front, released in advance for people to trial, and allow for some expanded functionality. An example is the Given that the next F# language version is likely to be F# 5.0, we do have an opportunity to do a similar thing. |
I have posted a lot about this issue because while
let l=[1;2]
l.[0..-1] // gives [], the natural initial condition
l.[0..0] // gives [1]
l.[0..1] // gives [1;2] If
|
I'm afraid I still don't understand what "logical" means in this context. What logic is at play, how is that expected by users, and why is it important that it remains so aside from compatibility? Put differently, I find the entire notion of |
Logical here: allowing a clear definition that can be reasononed about, which extends to its natural initial condition of an empty sequence.
I gave an example just above: if n is a position in the string s, you would expect
Since F#'s range end is inclusive and Python's is exclusive, Just think about when you want slices to return empty sequences, and what numbers in F# would give this. In Python it's |
@charlesroddie Just so I've got it right, you don't have any opposition to the concept of allowing "from the end" slicing, yes? Just want to make sure syntax v. semantics is cleanly separated here.
There are a three problems with this:
I don't really see this as a compelling argument against |
Also to consider- negative index at the end of a range could be done, but a negative index by itself could not. This implies the need for something else like ^x, otherwise it would have an inconsistent feel and probably suboptimal experience. |
No objection.
Do you seriously want to say that there is no position in a string such that the substring consisting of characters before this position is empty? (/List/Array...) This will give a languge that is less logical than Python and C#, a language that is supposed to be mathsy and recursive but doesn't understand base cases.
This is only for |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@charlesroddie This is now far off-topic. I'll ask that if you wish to continue this discussion, it be done in a separate issue, perhaps in the fslang-design repo here: fsharp/fslang-design#217 |
@dsyme any thoughts on this with |
|
This is acceptable with the I believe the biggest problem will be about lexing the (TBH I would even be happy with the new feature breaking the 0.00000001% of code that might define that operator) |
Comments on the current design in the RFC: Do you think
xs.[^2..^1] This will call our A similar thing can also be said when getting the slice when calculating the from-end index. As an example after de-sugaring: xs.GetSlice(Some(xs.GetReverseIndex(0, 2)), Some(xs.GetReverseIndex(0, 1)) For a F# We need to strongly consider a design that only makes a single call to an extension method in order to avoid issues of potentially doing a lot of work on a slice. |
Closing this as it is now implemented and will be in the next F# vNext preview. |
@cartermp when will this feature be out of preview? You still can't use it with Lang Version 6.0. I would hope after 2.5 years this would maybe be ready 😄 |
Submitted by Gustavo Guerra on 11/6/2014 12:00:00 AM
39 votes on UserVoice prior to migration
Example: permit usage of
a.[..-2]
instead ofa.[..a.Length-1]
The compiler could just do that conversion behind the scenes, so it would work with existing types that have custom indexing and slicingOriginal UserVoice Submission
Archived Uservoice Comments
The text was updated successfully, but these errors were encountered: