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

Add String#enclosed_with? #5950

Closed
wants to merge 2 commits into from
Closed

Conversation

j8r
Copy link
Contributor

@j8r j8r commented Apr 15, 2018

A method that combine starts_with? and ends_with?.
This is of course not an essential feature, but I find it quite elegant - specially when used with when inside case expressions.
Be free to not merge.

@sdogruyol
Copy link
Member

I don't think this adds a visible value to what we currently have with starts_with? / ends_with?.

@j8r
Copy link
Contributor Author

j8r commented Apr 15, 2018

@sdogruyol Agree - I've added custom methods for String and Regex to merge both starts_with? and ends_with? programmatically. We can expect a performance gain.

@ysbaddaden
Copy link
Contributor

Sorry, I hardly see the value.

@j8r
Copy link
Contributor Author

j8r commented Apr 15, 2018

For example in my code I've strings that can be simple/double quoted, or not. Before this, it was a lot more verbose.
The value is readability and performance (except Char for now):

if str.starts_with?('"') && str.ends_with?("'")
  something
elsif str.starts_with?('\'') && str.ends_with?('\'')
  other_thing
end

Now I just:

case str
when .enclosed_with? '"'  then something
when .enclosed_with? '\'' then other_thing
end

@straight-shoota
Copy link
Member

There is something to it when start and end are the same...

@asterite
Copy link
Member

Use &&. Define a helper method. Let's not clutter the std.

@asterite asterite closed this Apr 16, 2018
@j8r
Copy link
Contributor Author

j8r commented Apr 16, 2018

I've now deleted the two arguments overload, that is not really needed I admit.

The String, Regex implementations allow us to gain significant performance - even the Charone.

class String
  def enclosed_with?(char : Char)
    starts_with?(char) && ends_with?(char)
  end

  def enclosed_with?(str : String)
    return false if str.bytesize > bytesize
    (to_unsafe + bytesize - str.bytesize).memcmp(str.to_unsafe, str.bytesize) == to_unsafe.memcmp(str.to_unsafe, str.bytesize) == 0
  end

  def enclosed_with?(re : Regex)
    !!($~ = /^#{re}.*#{re}\z/.match(self))
  end

  def starts_with?(re : Regex)
    !!($~ = re.match_at_byte_index(self, 0, Regex::Options::ANCHORED))
  end

  def ends_with?(re : Regex)
    !!($~ = /#{re}\z/.match(self))
  end
end

require "benchmark"

CHAR = '_'
BAD_CHAR = '#'
STR_DELMIMITER = "_TEST_"
STRING = STR_DELMIMITER * 9999
REGEX = /_TEST_/

puts "Char: "
Benchmark.ips do |x|
  x.report " enclosed_with?" do
    999.times do
      STRING.enclosed_with? CHAR
      STRING.enclosed_with? BAD_CHAR
    end
  end

  x.report "starts_with? and ends_with?" do
    999.times do
      STRING.starts_with?(CHAR) && STRING.ends_with?(CHAR)
      STRING.starts_with?(BAD_CHAR) && STRING.ends_with?(CHAR)
    end
  end
end

puts "String: "
Benchmark.ips do |x|
  x.report "enclosed_with?" do
    999.times do
      STRING.enclosed_with? STR_DELMIMITER
    end
  end

  x.report "String: starts_with? and ends_with?" do
    999.times do
      STRING.starts_with?(STR_DELMIMITER) && STRING.ends_with?(STR_DELMIMITER)
    end
  end
end

puts "Regex: "
Benchmark.ips do |x|
  x.report "enclosed_with?" do
    999.times do
      STRING.enclosed_with? REGEX
    end
  end

  x.report "starts_with? and ends_with?" do
    999.times do
      STRING.starts_with?(REGEX) && STRING.ends_with?(REGEX)
    end
  end
end

Results:

Char: 
             enclosed_with? 159.81k (  6.26µs) (± 3.10%)       fastest
starts_with? and ends_with? 145.35k (  6.88µs) (± 1.93%)  1.10× slower
String: 
             enclosed_with?   2.07M (483.75ns) (± 3.47%)       fastest
starts_with? and ends_with? 591.67k (  1.69µs) (± 8.69%)  3.49× slower
Regex: 
             enclosed_with?   5.98  (167.29ms) (±10.53%)       fastest
starts_with? and ends_with?   0.84  (   1.2s ) (± 3.51%)  7.15× slower

@j8r
Copy link
Contributor Author

j8r commented Apr 17, 2018

@asterite can you consider reopening this? I've done some cleanups. I guess the commits doesn't appear here because the PR is closed...

@bcardiff
Copy link
Member

Sorry @j8r, but enough people have raised that adding enclosed_with? does not improve the std. For readability of code it can be easily added. If there are some effort that will be great for certain usages maybe a shard is a better fit. If so, feel free to drop a link here for reference.

I hope to see you in other issue & PR around 🙇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants