Idiomatic rust rewrite of Rob Pikes regex - regex

In the book The Practice of Programming, there's a short snippet of C code that implements a regex matcher. Brian W Kernighan has expanded on that chapter and published it online at https://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
Robs C code relies on the '\0' sentinels for length matching, but that won't work in Rust. So my port of the code ends up with lots of length checks.
I use slices to byte arrays to simplify the task a bit, instead of supporting utf-8 encoded strings.
The implementation consists of three functions re_match, match_here and match_star. I use ranges for iterating over the slices.
// Search for regex anywhere in the text.
fn re_match(regex: &[u8], text: &[u8]) -> bool {
if regex.len() > 0 && regex[0] == b'^' {
return match_here(&regex[1..], &text);
}
// We need to check even if text is empty
if match_here(regex, text) {
return true
}
for i in 0..text.len() {
if match_here(regex, &text[i..]) {
return true
}
}
false
}
// Search for regex at beginning of text
fn match_here(regex: &[u8], text: &[u8]) -> bool {
if regex.len() == 0 {
return true;
}
if regex.len() > 1 && regex[1] == b'*' {
return match_star(regex[0], &regex[2..], text);
}
if regex.len() == 1 && regex[0] == b'$' {
return text.len() == 0;
}
if text.len() > 0 && (regex[0] == b'.' || regex[0] == text[0]) {
return match_here(&regex[1..], &text[1..]);
}
false
}
// Search for c* regex at beginning of text.
fn match_star(c: u8, regex: &[u8], text: &[u8]) -> bool {
if match_here(regex, text) {
return true;
}
let mut i = 0;
while i < text.len() && (text[i] == c || c == b'.') {
if match_here(regex, &text[i..]) {
return true;
}
i += 1;
}
false
}
Question
How can I rewrite these kind of functions to not need so many length checks?
META: Should I use iterators instead of slices as parameters? When choose one over the other?

For the match_here I would use match with the underappreciated slice-pattern syntax. That would avoid most len() uses. Using is_empty() is nicer than len() == 0:
fn match_here(regex: &[u8], text: &[u8]) -> bool {
match regex {
&[] => true,
&[b'$'] => {
text.is_empty()
}
&[z, b'*', ref tail # ..] => {
match_star(z, tail, text)
}
&[z, ref tail # ..] if if !text.is_empty() && (z == b'.' || z == text[0]) => {
match_here(tail, &text[1..])
}
_ => false,
}
}
Or if you feel fancy you can do a double match:
fn match_here(regex: &[u8], text: &[u8]) -> bool {
match (regex, text) {
(&[], _) => true,
(&[b'$'], &[]) => true,
(&[b'$'], _) => false,
(&[z, b'*', ref tail # ..], txt) => {
match_star(z, tail, txt)
}
(&[z, ref tail # ..], &[]) => false,
(&[z, ref tail # ..], &[tz, ref ttail # ..]) => if z == b'.' || z == tz => {
match_here(tail, ttail)
}
_ => false,
}
}
The cool thing about this latter option is that since you are never using the index operator [x] you are sure you will never go out of bounds, without ever checking the len() of your slices.
The len() of the other functions, I don't particularly see them as non-idiomatic. They may be rewritten in a more rusty way, but then the equivalence to the C code would not be so obvious. You would need to stop and think!
About using iterators or slices, I personally prefer slices for this kind of things. The problem with iterators is the backbuffer, for example, to check for the x* you need to take two bytes from the iterator, but then, if the second one is not a * you have to put it back... Naturally you can use Iterator::peek but that will only give you one element. If at any time you need to look ahead two bytes, you have a problem.
So unless you need to parse a very big input (> hundreds of MBs) I would stick to the plain slices.

Related

Split list when predicate is true

Does Kotlin provide a mutation function to split a list when a specific predicate is true?
In the following example the list should be split when the element is a ..
The result should be of the type List<List<String>>.
// input list
val list = listOf(
"This is", "the", "first sentence", ".",
"And", "now there is", "a second", "one", ".",
"Nice", "."
)
// the following should be the result of the transformation
listOf(
listOf("This is", "the", "first sentence"),
listOf("And", "now there is", "a second", "one"),
listOf("Nice")
)
I need something like list.splitWhen { it == "." }
Does Kotlin provide a mutation function to split a list when a
specific predicate is true?
The closest one I have heard of is partition(), however I don't think it will work in your case.
I have made and have briefly tested 3 higher order extension functions, which gives the same expected output.
Solution 1: Straightforward approach
inline fun List<String>.splitWhen(predicate: (String)->Boolean):List<List<String>> {
val list = mutableListOf<MutableList<String>>()
var needNewList = false
forEach {
string->
if(!predicate(string)){
if(needNewList||list.isEmpty()){
list.add(mutableListOf(string))
needNewList= false
}
else {
list.last().add(string)
}
}
else {
/* When a delimiter is found */
needNewList = true
}
}
return list
}
Solution 2: Pair based approach
inline fun List<String>.splitWhen(predicate: (String)->Boolean):List<List<String>> {
val list = mutableListOf<List<String>>()
withIndex()
.filter { indexedValue -> predicate(indexedValue.value) || indexedValue.index==0 || indexedValue.index==size-1} // Just getting the delimiters with their index; Include 0 and last -- so to not ignore it while pairing later on
.zipWithNext() // zip the IndexValue with the adjacent one so to later remove continuous delimiters; Example: Indices : 0,1,2,5,7 -> (0,1),(1,2),(2,5),(5,7)
.filter { pair-> pair.first.index + 1 != pair.second.index } // Getting rid of continuous delimiters; Example: (".",".") will be removed, where "." is the delimiter
.forEach{pair->
val startIndex = if(predicate(pair.first.value)) pair.first.index+1 else pair.first.index // Trying to not consider delimiters
val endIndex = if(!predicate(pair.second.value) && pair.second.index==size-1) pair.second.index+1 else pair.second.index // subList() endIndex is exclusive
list.add(subList(startIndex,endIndex)) // Adding the relevant sub-list
}
return list
}
Solution 3: Check next value if delimiter found approach
inline fun List<String>.splitWhen(predicate: (String)-> Boolean):List<List<String>> =
foldIndexed(mutableListOf<MutableList<String>>(),{index, list, string->
when {
predicate(string) -> if(index<size-1 && !predicate(get(index+1))) list.add(mutableListOf()) // Adds a new List within the output List; To prevent continuous delimiters -- !predicate(get(index+1))
list.isNotEmpty() -> list.last().add(string) // Just adding it to lastly added sub-list, as the string is not a delimiter
else -> list.add(mutableListOf(string)) // Happens for the first String
}
list})
Simply call list.splitWhen{it=="delimiter"}. Solution 3 looks more syntactic sugar. Apart from it, you can do some performance test to check which one performs well.
Note: I have done some brief tests which you can have a look via Kotlin Playground or via Github gist.

Backspace String Compare Leetcode Question

I have a question about the following problem on Leetcode:
Given two strings S and T, return if they are equal when both are typed into empty text editors. # means a backspace character.
Example 1:
Input: S = "ab#c", T = "ad#c"
Output: true
Explanation: Both S and T become "ac".
Example 2:
Input: S = "ab##", T = "c#d#"
Output: true
Explanation: Both S and T become "".
Example 3:
Input: S = "a##c", T = "#a#c"
Output: true
Explanation: Both S and T become "c".
Example 4:
Input: S = "a#c", T = "b"
Output: false
Explanation: S becomes "c" while T becomes "b".
Note:
1 <= S.length <= 200
1 <= T.length <= 200
S and T only contain lowercase letters and '#' characters.
Follow up:
Can you solve it in O(N) time and O(1) space?
My answer:
def backspace_compare(s, t)
if (s.match?(/[^#[a-z]]/) || t.match?(/[^#[a-z]]/)) || (s.length > 200 || t.length > 200)
return "fail"
else
rubular = /^[\#]+|([^\#](\g<1>)*[\#]+)/
if s.match?(/#/) && t.match?(/#/)
s.gsub(rubular, '') == t.gsub(rubular, '')
else
new_s = s.match?(/#/) ? s.gsub(rubular, '') : s
new_t = t.match?(/#/) ? t.gsub(rubular, '') : t
new_s == new_t
end
end
end
It works in the terminal and passes the given examples, but when I submit it on leetcode it tells me Time Limit Exceeded. I tried shortening it to:
rubular = /^[\#]+|([^\#](\g<1>)*[\#]+)/
new_s = s.match?(/#/) ? s.gsub(rubular, '') : s
new_t = t.match?(/#/) ? t.gsub(rubular, '') : t
new_s == new_t
But also the same error.
So far, I believe my code fulfills the O(n) time, because there are only two ternary operators, which overall is O(n). I'm making 3 assignments and one comparison, so I believe that fulfills the O(1) space complexity.
I have no clue how to proceed beyond this, been working on it for a good 2 hours..
Please point out if there are any mistakes in my code, and how I am able to fix it.
Thank you! :)
Keep in mind that with N <= 200, your problem is more likely to be linear coefficient, not algorithm complexity. O(N) space is immaterial for this; with only 400 chars total, space is not an issue. You have six regex matches, two of which are redundant. More important, regex is slow processing for such a specific application.
For speed, drop the regex stuff and do this one of the straightforward, brute-force ways: run through each string in order, applying the backspaces as appropriate. For instance, change both the backspace and the preceding letter to spaces. At the end of your checking, remove all the spaces in making a new string. Do this with both S and T; compare those for equality.
It may be easiest to start at the end of the string and work towards the beginning:
def process(str)
n = 0
str.reverse.each_char.with_object('') do |c,s|
if c == '#'
n += 1
else
n.zero? ? (s << c) : n -= 1
end
end.reverse
end
%w|ab#c ad#c ab## c#d# a##c #a#c a#c b|.each_slice(2) do |s1, s2|
puts "\"%s\" -> \"%s\", \"%s\" -> \"%s\" %s" %
[s1, process(s1), s2, process(s2), (process(s1) == process(s2)).to_s]
end
"ab#c" -> "ac", "ad#c" -> "ac" true
"ab##" -> "", "c#d#" -> "" true
"a##c" -> "c", "#a#c" -> "c" true
"a#c" -> "c", "b" -> "b" false
Let's look at a longer string.
require 'time'
alpha = ('a'..'z').to_a
#=> ["a", "b", "c",..., "z"]
s = (10**6).times.with_object('') { |_,s|
s << (rand < 0.4 ? '#' : alpha.sample) }
#=> "h####fn#fjn#hw###axm...#zv#f#bhqsgoem#glljo"
s.size
#=> 1000000
s.count('#')
#=> 398351
and see how long it takes to process.
require 'time'
start_time = Time.now
(u = process(s)).size
#=> 203301
puts (Time.now - start_time).round(2)
#=> 0.28 (seconds)
u #=> "ffewuawhfa...qsgoeglljo"
As u will be missing the 398351 pound signs in s, plus an almost equal number of other characters removed by the pound signs, we would expect u.size to be about:
10**6 - 2 * s.count('#')
#=> 203298
In fact, u.size #=> 203301, meaning that, at the end, 203301 - 203298 #=> 3 pound signs were unable to remove a character from s.
In fact, process can be simplified. I leave that as an exercise for the reader.
class Solution {
public boolean backspaceCompare(String s, String t) {
try {
Stack<Character> st1 = new Stack<>();
Stack<Character> st2 = new Stack<>();
st1 = convertToStack(s);
st2 = convertToStack(t);
if (st1.size() != st2.size()) {
return false;
} else {
int length = st1.size();
for (int i = 0; i < length; i++) {
if (st1.peek() != st2.peek())
return false;
else {
st1.pop();
st2.pop();
}
if (st1.isEmpty() && st2.isEmpty())
return true;
}
}
} catch (Exception e) {
System.out.print(e);
}
return true;
}
public Stack<Character> convertToStack(String s){
Stack<Character> st1 = new Stack<>();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != '#') {
st1.push(s.charAt(i));
} else if (st1.empty()) {
continue;
} else {
st1.pop();
}
}
return st1;
}
}

filter/map structure to map/guard structure in Scala

I have a summaryPool mutable map that maps a String to a Summary object.
The function namesToSummary has two parameters, the first one is a series of names (in Iterable[String]), and the second one is the summaryPool. What it does is that it returns a series of Summary that corresponds the names.
It's a little bit more complicated, as the name should be checked using regular expression to extract the information that used to be the key to the summaryPool.
For example, "summary1b" should be checked to get "summary1" and "b"; the "summary1" is the key to the pool. In some cases, there may not be the "b" appended.
My implementation uses isSummaryPool function to filter out wrongly formatted name, or the name that is not in the pool. Then, I use map to get the copy of Summary object in the pool.
import scala.collection.mutable.{Map => mm}
def namesToSummaries(names: Iterable[String], summaryPool: mm[String, Summary]) = {
val namePattern = """([a-zA-Z]+\d+)([a-z])?""".r
def inSummaryPool(name: String) = {
name match {
case namePattern(summaryName, summaryType) => {
if (summaryPool.contains(summaryName)) true
else false
}
case _ => false
}
}
names filter inSummaryPool map { name =>
name match {
case namePattern(summaryName, summaryType) => {
var sType = summaryType
if (sType == null || !(sType == "b" || sType == "l")) sType = "b"
summaryPool.get(summaryName).get.copy(sType)
}
}
}
}
It works fine, but I don't like the implementation as it checks regular expression matching twice.
I think I can integrate the filter/map into map with guard. In order to do so, I thinK I may need to implement similar to this:
import scala.collection.mutable.{Map => mm}
def namesToSummaries(names: Iterable[String], summaryPool: mm[String, Summary]) = {
val namePattern = """([a-zA-Z]+\d+)([a-z])?""".r
names map { name =>
name match {
case namePattern(summaryName, summaryType) => {
if (summaryPool.contains(summaryName)) {
var sType = summaryType
if (sType == null || !(sType == "b" || sType == "l")) sType = "b"
summaryPool.get(summaryName).get.copy(sType)
}
else
???
}
case _ => ???
}
}
}
I'm not sure what expression should be given in ??? to teach Scala to ignore these cases.
What might be the solution?
EDIT1
I can think about making a ListBuffer object to add Summary object when necessary.
But, I'm not sure about the case when the pattern does not match.
val list: ListBuffer
names foreach { name =>
name match {
case namePattern(summaryName, summaryType) => {
if (summaryPool.contains(summaryName)) {
var sType = summaryType
if (sType == null || !(sType == "b" || sType == "l")) sType = "b"
list += summaryPool.get(summaryName).get.copy(sType)
}
}
case _ => ???
}
}
}
EDIT2
From Shadowlands' answer, flatMap with None return works fine.
def namesToSummaries(names: Iterable[String], summaryPool: mm[String, Summary]) = {
val namePattern = """([a-zA-Z]+\d+)([a-z])?""".r
names flatMap { name =>
name match {
case namePattern(summaryName, summaryType) => {
if (summaryPool.contains(summaryName)) {
var sType = summaryType
if (sType == null || !(sType == "b" || sType == "l")) sType = "b"
Some(summaryPool.get(summaryName).get.copy())
}
else None
}
case _ => None
}
}
}
EDIT3
From Jilen's hint, collect seems to be the good answer to reduce more lines of code.
def namesToSummaries(names: Iterable[String], summaryPool: mm[String, Summary]) = {
val namePattern = """([a-zA-Z]+\d+)([a-z])?""".r
names collect { name =>
name match {
case namePattern(summaryName, summaryType) if (summaryPool.contains(summaryName)) => {
var sType = summaryType
if (sType == null || !(sType == "b" || sType == "l")) sType = "b"
summaryPool.get(summaryName).get.copy()
}
}
}
}
However, this code in IntelliJ 14 shows false positive error: this is a bug report (https://youtrack.jetbrains.com/issue/SCL-9094#).
Instead of calling map on the names, try using flatMap. Wrap your successful cases in Some(...), and the ??? becomes None. The 'flattening' part of the flatMap will reduce the 'mapped' Iterable[Option[String]] back to an Iterable[String], ditching all the None cases.
Edit: I didn't drill into your code quite carefully enough - in the 'successful' case you appear to be doing pure side-effecting stuff (ie. updating the mutable map), not returning a result of any kind.
You could instead return a (summaryName, summaryType) tuple at this point (wrapped in Some) and apply the side-effecting code to the contents of the resulting flatMap (probably my preference as being a slightly more functional style), or simply go back to using map and just write _ (meaning here: 'do nothing - ignore any result') instead of ???.

Number of occurrences of substring in string in Swift

My main string is "hello Swift Swift and Swift" and substring is Swift.
I need to get the number of times the substring "Swift" occurs in the mentioned string.
This code can determine whether the pattern exists.
var string = "hello Swift Swift and Swift"
if string.rangeOfString("Swift") != nil {
println("exists")
}
Now I need to know the number of occurrence.
A simple approach would be to split on "Swift", and subtract 1 from the number of parts:
let s = "hello Swift Swift and Swift"
let tok = s.components(separatedBy:"Swift")
print(tok.count-1)
This code prints 3.
Edit: Before Swift 3 syntax the code looked like this:
let tok = s.componentsSeparatedByString("Swift")
Should you want to count characters rather than substrings:
extension String {
func count(of needle: Character) -> Int {
return reduce(0) {
$1 == needle ? $0 + 1 : $0
}
}
}
Optimising dwsolbergs solution to count faster. Also faster than componentsSeparatedByString.
extension String {
/// stringToFind must be at least 1 character.
func countInstances(of stringToFind: String) -> Int {
assert(!stringToFind.isEmpty)
var count = 0
var searchRange: Range<String.Index>?
while let foundRange = range(of: stringToFind, options: [], range: searchRange) {
count += 1
searchRange = Range(uncheckedBounds: (lower: foundRange.upperBound, upper: endIndex))
}
return count
}
}
Usage:
// return 2
"aaaa".countInstances(of: "aa")
If you want to ignore accents, you may replace options: [] with options: .diacriticInsensitive like dwsolbergs did.
If you want to ignore case, you may replace options: [] with options: .caseInsensitive like ConfusionTowers suggested.
If you want to ignore both accents and case, you may replace options: [] with options: [.caseInsensitive, .diacriticInsensitive] like ConfusionTowers suggested.
If, on the other hand, you want the fastest comparison possible and you can guarantee some canonical form for composed character sequences, then you may consider option .literal and it will only perform exact matchs.
Swift 5 Extension
extension String {
func numberOfOccurrencesOf(string: String) -> Int {
return self.components(separatedBy:string).count - 1
}
}
Example use
let string = "hello Swift Swift and Swift"
let numberOfOccurrences = string.numberOfOccurrencesOf(string: "Swift")
// numberOfOccurrences = 3
I'd recommend an extension to string in Swift 3 such as:
extension String {
func countInstances(of stringToFind: String) -> Int {
var stringToSearch = self
var count = 0
while let foundRange = stringToSearch.range(of: stringToFind, options: .diacriticInsensitive) {
stringToSearch = stringToSearch.replacingCharacters(in: foundRange, with: "")
count += 1
}
return count
}
}
It's a loop that finds and removes each instance of the stringToFind, incrementing the count on each go-round. Once the searchString no longer contains any stringToFind, the loop breaks and the count returns.
Note that I'm using .diacriticInsensitive so it ignore accents (for example résume and resume would both be found). You might want to add or change the options depending on the types of strings you want to find.
I needed a way to count substrings that may contain the start of the next matched substring. Leveraging dwsolbergs extension and Strings range(of:options:range:locale:) method I came up with this String extension
extension String
{
/**
Counts the occurrences of a given substring by calling Strings `range(of:options:range:locale:)` method multiple times.
- Parameter substring : The string to search for, optional for convenience
- Parameter allowOverlap : Bool flag indicating whether the matched substrings may overlap. Count of "🐼🐼" in "🐼🐼🐼🐼" is 2 if allowOverlap is **false**, and 3 if it is **true**
- Parameter options : String compare-options to use while counting
- Parameter range : An optional range to limit the search, default is **nil**, meaning search whole string
- Parameter locale : Locale to use while counting
- Returns : The number of occurrences of the substring in this String
*/
public func count(
occurrencesOf substring: String?,
allowOverlap: Bool = false,
options: String.CompareOptions = [],
range searchRange: Range<String.Index>? = nil,
locale: Locale? = nil) -> Int
{
guard let substring = substring, !substring.isEmpty else { return 0 }
var count = 0
let searchRange = searchRange ?? startIndex..<endIndex
var searchStartIndex = searchRange.lowerBound
let searchEndIndex = searchRange.upperBound
while let rangeFound = range(of: substring, options: options, range: searchStartIndex..<searchEndIndex, locale: locale)
{
count += 1
if allowOverlap
{
searchStartIndex = index(rangeFound.lowerBound, offsetBy: 1)
}
else
{
searchStartIndex = rangeFound.upperBound
}
}
return count
}
}
why not just use some length maths??
extension String {
func occurences(of search:String) -> Int {
guard search.count > 0 else {
preconditionFailure()
}
let shrunk = self.replacingOccurrences(of: search, with: "")
return (self.count - shrunk.count)/search.count
}
}
Try this
var mainString = "hello Swift Swift and Swift"
var count = 0
mainString.enumerateSubstrings(in: mainString.startIndex..<mainString.endIndex, options: .byWords) { (subString, subStringRange, enclosingRange, stop) in
if case let s? = subString{
if s.caseInsensitiveCompare("swift") == .orderedSame{
count += 1
}
}
}
print(count)
For the sake of completeness – and because there is a regex tag – this is a solution with Regular Expression
let string = "hello Swift Swift and Swift"
let regex = try! NSRegularExpression(pattern: "swift", options: .caseInsensitive)
let numberOfOccurrences = regex.numberOfMatches(in: string, range: NSRange(string.startIndex..., in: string))
The option .caseInsensitive is optional.
My solution, maybe it will be better to use String.Index instead of Int range but I think in such way it is a bit easier to read.
extension String {
func count(of char: Character, range: (Int, Int)? = nil) -> Int {
let range = range ?? (0, self.count)
return self.enumerated().reduce(0) {
guard ($1.0 >= range.0) && ($1.0 < range.1) else { return $0 }
return ($1.1 == char) ? $0 + 1 : $0
}
}
}
Solution which uses a higher order functions
func subStringCount(str: String, substr: String) -> Int {
{ $0.isEmpty ? 0 : $0.count - 1 } ( str.components(separatedBy: substr))
}
Unit Tests
import XCTest
class HigherOrderFunctions: XCTestCase {
func testSubstringWhichIsPresentInString() {
XCTAssertEqual(subStringCount(str: "hello Swift Swift and Swift", substr: "Swift"), 3)
}
func testSubstringWhichIsNotPresentInString() {
XCTAssertEqual(subStringCount(str: "hello", substr: "Swift"), 0)
}
}
Another way using RegexBuilder in iOS 16+ & swift 5.7+.
import RegexBuilder
let text = "hello Swift Swift and Swift"
let match = text.matches(of: Regex{"Swift"})
print(match.count) // prints 3
Using this as a function
func countSubstrings(string : String, subString : String)-> Int{
return string.matches(of: Regex{subString}).count
}
print(countSubstrings(string: text, subString: "Swift")) //prints 3
Using this as an Extension
extension String {
func countSubstrings(subString : String)-> Int{
return self.matches(of: Regex{subString}).count
}
}
print(text.countSubstrings(subString: "Swift")) // prints 3

C++ to Ruby: find substring in a string

I learned Rails and now would like to expand my knowledge of Ruby. So I'm doing some C++ exercises in Ruby. Specifically I need to find if a substring exists in a string. If it does I need it to return its starting index. If it doesn't exist have it return -1. I came up with a Ruby solution that's very similar to C++ and was wondering if there's a "better", more idiomatic solution in Ruby?
C++
int find(char str[], char sub_str[])
{
int str_length = strlen(str);
int sub_str_length = strlen(sub_str);
bool match = false;
for(int i=0; i<str_length; i++)
{
if(str[i] == sub_str[0])
{
for(int j=1; j<sub_str_length; j++)
{
if(str[i+j] == sub_str[j])
match = true;
else
{
match = false;
break;
}
}
if(match)
return i;
}
}
return -1;
}
Ruby
def find_sub_str(str, sub_str)
match = false
for i in 0...str.length
if str[i] == sub_str[0]
for j in 1...sub_str.length
if str[i+j] == sub_str[j]
match = true
else
match = false
break
end
end
if match == true
return i
end
end
end
return -1
end
You can use the index method of String. It returns nil on failure to match, which is more idiomatic Ruby than returning -1.
"SubString".index("String") # -> 3
"SubString".index("C++") # -> nil
You could wrap it in a test that returns -1 for nil if you really wanted this behavior.
Don’t use for in Ruby, it just calls each and doesn’t introduce scope. So for i in 0...str.length becomes (0...str.length).each do |i|.
Higher-order functions are your friend! Using each_cons & find_index makes things much cleaner (study Enumerable, it’s home to many useful methods):
def find_sub_str(str, sub_str)
str.chars.each_cons(sub_str.length).find_index do |s|
s.join == sub_str
end
end
find_sub_str('foobar', 'ob') #=> 2
Just use Ruby core’s index :):
'foobar'.index('ob') #=> 2
Both #2 & #3 return nil, not -1, when there is no match. This is best because nil is falsey in Ruby.
#how if we use this solution, it gets the job done in O(n)
given_string = "Replace me with your code!"
chars_given_string = given.split('')
chars_of_substr = "ith".split('')
is_substr = false
ptr = 0
char_given.each do |i|
if ( i == substr[ptr])
ptr += 1
else
ptr = 0
end
is_substr = true if ptr == substr.length
break if ptr == substr.length
end
puts is_substr