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

Improve SubString nextind/prevind #24255

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions base/strings/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,41 @@ function thisind(s::SubString{String}, i::Integer)
j-offset
end

nextind(s::SubString, i::Integer) = nextind(s.string, i+s.offset)-s.offset
prevind(s::SubString, i::Integer) = prevind(s.string, i+s.offset)-s.offset
# need to define nextind and prevind only for SubString{String}
# as other cases are handled by definitions for AbstractString
Copy link
Member

@stevengj stevengj Dec 29, 2017

Choose a reason for hiding this comment

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

These generic definitions for SubString{T} should be more efficient than the generic nextind for AbstractString if there is an efficient nextind for T. So, better to leave them in?

Copy link
Member

Choose a reason for hiding this comment

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

Oh never mind, I guess we already discussed that above?:

Copy link
Member Author

Choose a reason for hiding this comment

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

The PR is outdated (it was prepared before "string overhaul"), but the problem it solved is still present in the new code.

Therefore my thinking is to first decide on the desired behavior (I guess @StefanKarpinski will have an opinion here).

Next there is an issue of maximum efficiency of implementation. My experience is that at least SubString{String} should have a specialized method - as this will be most common case in practice and probably there is a room for efficiency improvement by using custom code.

But first we should have a defined contract we want the implementation to follow.

function nextind(s::SubString{String}, i::Integer)
# make sure that nonnegative value is returned
j = Int(i)
j < 1 && return 1
# the transformation below is valid if j>=0
nextind(s.string, j+s.offset)-s.offset
end

function prevind(s::SubString{String}, i::Integer)
e = endof(s)
# make sure that value not greater than endof(s) is returned
j = Int(i)
j > e && return e
# the transformation below is valid if j<=endof(s)+1
prevind(s.string, j+s.offset)-s.offset
end

function nextind(s::SubString{String}, i::Integer, nchar::Integer)
j = Int(i)
# if j < 1 the first valid value of j is the same as for j equal to 0
# and the transformation below is valid if j>=0
j < 0 && (j = 0)
nextind(s.string, j+s.offset, nchar)-s.offset
end

function prevind(s::SubString{String}, i::Integer, nchar::Integer)
e = endof(s)
j = Int(i)
# if j > endof(s) the first valid value of j is the same as for j equal to endof(s)+1
# and the transformation below is valid if j<=endof(s)+1
j > e && (j = e+1)
prevind(s.string, j+s.offset, nchar)-s.offset
end

function getindex(s::AbstractString, r::UnitRange{Int})
checkbounds(s, r) || throw(BoundsError(s, r))
Expand Down
4 changes: 3 additions & 1 deletion stdlib/Test/src/Test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1380,7 +1380,9 @@ the `AbstractString` interface, in order to ensure that functions can work
with string types besides the standard `String` type.
"""
struct GenericString <: AbstractString
string::AbstractString
string::String

GenericString(s::AbstractString) = new(String(s))
end
Base.endof(s::GenericString) = endof(s.string)
Base.next(s::GenericString, i::Int) = next(s.string, i)
Expand Down
62 changes: 60 additions & 2 deletions test/strings/basic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -592,8 +592,9 @@ end
end

@testset "prevind and nextind" begin
let strs = Any["∀α>β:α+1>β", GenericString("∀α>β:α+1>β")]
for i in 1:2
let str = "∀α>β:α+1>β", strs = Any[str, GenericString(str), SubString(str, 1),
SubString(GenericString(str), 1)]
for i in 1:length(strs)
@test prevind(strs[i], 1) == 0
@test prevind(strs[i], 1, 1) == 0
@test prevind(strs[i], 2) == 1
Expand Down Expand Up @@ -653,12 +654,69 @@ end
@test nextind(strs[i], x, j) == n
end
end

let ss = SubString(strs[i], 1, 4)
@test prevind(ss, 0) < 1
@test prevind(ss, 0, 1) < 1
@test prevind(ss, 1, 2) < 1
@test prevind(ss, 0, 2) < 1
end
let ss = SubString(strs[i], 7, 10)
@test prevind(ss, 1) < 1
@test prevind(ss, 1, 1) < 1
@test prevind(ss, 0) < 1
@test prevind(ss, 0, 1) < 1
@test prevind(ss, 1, 2) < 1
@test nextind(ss, 4) > endof(ss)
@test nextind(ss, 4, 1) > endof(ss)
@test nextind(ss, 9) > endof(ss)
@test nextind(ss, 9, 1) > endof(ss)
end
end

@test prevind(strs[1], -1) == -2
@test prevind(strs[1], -1, 1) == -2

@test prevind(strs[2], -1) == 0
@test prevind(strs[2], -1, 1) == 0

@test prevind(strs[3], -1) == -2
@test prevind(strs[3], -1, 1) == -2

@test prevind(strs[4], -4) == 0
@test prevind(strs[4], -4, 1) == 0
end

let strs = Any["1234567", GenericString("1234567")]
for i in 1:length(strs)
@test prevind(SubString(strs[i], 1, 3), 10) == 3
@test prevind(SubString(strs[i], 1, 3), 10, 1) == 3
@test prevind(SubString(strs[i], 1, 3), 10, 2) == 2
@test prevind(SubString(strs[i], 1, 3), 3) == 2
@test prevind(SubString(strs[i], 1, 3), 2) == 1
@test prevind(SubString(strs[i], 1, 3), 1) == 0
@test prevind(SubString(strs[i], 1, 3), 3, 1) == 2
@test prevind(SubString(strs[i], 1, 3), 2, 1) == 1
@test prevind(SubString(strs[i], 1, 3), 1, 1) == 0
@test prevind(SubString(strs[i], 1, 3), 3, 2) == 1
@test prevind(SubString(strs[i], 1, 3), 2, 2) == 0

@test nextind(SubString(strs[i], 3), -6) == 1
@test nextind(SubString(strs[i], 3), -6, 1) == 1
@test nextind(SubString(strs[i], 3), -6, 2) == 2
@test nextind(SubString(strs[i], 3), 1) == 2
@test nextind(SubString(strs[i], 3), 2) == 3
@test nextind(SubString(strs[i], 3), 3) == 4
@test nextind(SubString(strs[i], 3), 4) == 5
@test nextind(SubString(strs[i], 3), 1, 1) == 2
@test nextind(SubString(strs[i], 3), 2, 1) == 3
@test nextind(SubString(strs[i], 3), 3, 1) == 4
@test nextind(SubString(strs[i], 3), 4, 1) == 5
@test nextind(SubString(strs[i], 3), 1, 2) == 3
@test nextind(SubString(strs[i], 3), 2, 2) == 4
@test nextind(SubString(strs[i], 3), 3, 2) == 5
@test nextind(SubString(strs[i], 3), 4, 2) == 6
end
end
end

Expand Down