String count without using ".length" or ".size" methods? - ruby

I am new to the ruby and was practicing a code. I want to count the letters in a string by a self written code, without using #length or #size method. I have searched online but am unable to find anything relating to my query. I would appreciate if anyone could help me out in this simple program.

Other option, mapping String#chars with index then picking the last:
str = "123456"
str.chars.map.with_index { |_, i| i + 1 }.last
#=> 6
It generates an Array, but we are not looking for efficiency here.
Or even using String#index with offset:
str = "aaaa"
str.index(str[-1], -1) + 1
#=> 4
It looks for the index of the latest char starting from the end.

You can do that using any String method that enumerates characters. The most obvious is String#each_char, as #knut mentioned in a comment.
def str_length(str)
enum = str.each_char
n = 0
loop do
enum.next
n += 1
end
n
end
str_length "Zaphod"
#=> 6
Let's see what is happening here.
str = "Zaphod"
enum = str.each_char
#=> #<Enumerator: "123456":each_char>
n = 0
loop do
s = enum.next
n += 1
puts "s = #{s}, n = #{n}"
end
n #=> 6
prints
s = Z, n = 1
s = a, n = 2
s = p, n = 3
s = h, n = 4
s = o, n = 5
s = d, n = 6
See Enumerator#next. After enum.next #=> "d" is executed enum.next is executed once more, raising a StopIteration exception. That exception is handled by Kernel#loop by breaking out of the loop.
As I said at the outset, any String method could be used that enumerates characters. For example, enum = str.gsub(/./).
The same approach could be used for any class that implements a method that enumerates elements of a collection. For example, we could add a method to the Enumerable module, which would then be available for every class that includes that module.
module Enumerable
def my_length
enum = each
n = 0
loop do
enum.next
n += 1
end
n
end
end
[1,2,3,4].my_length
#=> 4
{ a: 1, b: 2 }.my_length
#=> 2
(1..5).my_length
#=> 5

Related

Ruby - Find the longest non-repeating substring in any given string

I am working on an assignment where I have to take user input of a string and search through it to find the longest non-repeating string in it. So for example:
If the string is:
"abcabcabcdef"
My output needs to be:
"abcdef is the longest substring at the value of 6 characters"
Here is my poorly made code:
class Homework_4
puts "Enter any string of alphabetical characters: "
user_input = gets
longest_str = 0
empty_string = ""
map = {}
i = 0
j = 0
def long_substr()
while j < str_len
if map.key?(user_input[j])
i = [map[user_input[j]], i].max
end
longest_str = [longest_str, j - i + 1].max
map[user_input[j]] = j + 1
j += 1
end
longest_str
end
long_substr(user_input)
end
I have been working on this for over 6 hours today and I just can't figure it out. It seems like the internet has many ways to do it. Almost all of them confuse me greatly and don't really explain what they're doing. I don't understand the syntax they use or any of the variables or conditions.
All I understand is that I need to create two indicators that go through the inputted string searching for a non-repeating substring (sliding window method). I don't understand how to create them, what to make them do or even how to make them find and build the longest substring. It is very confusing to try and read the code that is full of random letters, symbols, and conditions. I'm sure my code is all sorts of messed up but any help or tips that could point me in the right direction would be greatly appreciated!
def uniq?(s)
# All letters of s uniq?
return s.chars.uniq == s.chars
end
def subs(s)
# Return all substrings in s.
(0..s.length).inject([]){|ai,i|
(i..s.length - i).inject(ai){|aj,j|
aj << s[i,j]
}
}.uniq
end
def longest_usub(s)
# Return first longest substring of s.
substrings(s).inject{|res, s| (uniq?(s) and s.length > res.length) ? s : res}
end
ruby's inject is actually a reduce function, where inject(optional_start_value){<lambda expression>} - and the lambda expression is similar to Python's lambda x, y: <return expression using x and y> just that lambda expressions are strangely written in Ruby as {|x, y| <return expression using x and y>}.
Python's range(i, y) is Ruby's i..y.
Python's slicing s[i:j] is in Ruby s[i..j] or s[i,j].
<< means add to end of the array.
Second solution (inspired by #Rajagopalan's answer)
def usub(s)
# Return first chunk of uniq substring in s
arr = []
s.chars do |char|
break if arr.include? char
arr << char
end
arr.join
end
def usubs(s)
# Return each position's usub() in s
(0..s.length).to_a.map{|i| usub(s[i,s.length])}
end
def longest_usub(s)
# return the longest one of the usubs() over s
usubs(s).max_by(&:length)
end
then you can do:
longest_usub("abcabcabcdef")
## "abcdef"
I have asssumed that a string is defined to be repeating if it contains a substring s of one or one more characters that is followed by the same substring s, and that a string is non-repeating if it is not repeating.
A string is seen to be repeating if and only if it matches the regular expression
R = /([a-z]+)\1/
Demo
The regular expression reads, "match one or more letters that are saved to capture group one, then match the content of capture group 1".
For convenience we can construct a simple helper method.
def nonrepeating?(str)
!str.match? R
end
I will perform a binary search to find the longest non-repeating string. First, I need a second helper method:
def find_nonrepeating(str, len)
0.upto(str.size-len) do |i|
s = str[i,len]
return s if nonrepeating?(s)
end
nil
end
find_nonrepeating("abababc", 7) #=> nil
find_nonrepeating("abababc", 6) #=> nil
find_nonrepeating("abababc", 5) #=> nil
find_nonrepeating("abababc", 4) #=> "babc"
find_nonrepeating("abababc", 3) #=> "aba"
find_nonrepeating("abababc", 2) #=> "ab"
find_nonrepeating("abababc", 1) #=> "a"
We may now implement the binary search.
def longest(str)
longest = ''
low = 0
high = str.size - 1
while low < high
mid = (low + high)/2
s = find_nonrepeating(str, mid)
if s
longest = s
low = mid + 1
else
high = mid - 1
end
end
longest
end
longest("dabcabcdef")
#=> "bcabcdef"
a = "abcabcabcdef"
arr = []
words = []
b=a
a.length.times do
b.chars.each do |char|
break if arr.include? char
arr << char
end
words << arr.join
arr.clear
b=b.chars.drop(1).join
end
p words.map(&:chars).max_by(&:length).join
Output
"abcdef"

How to create a "clone"-able enumerator for external iteration?

I want to create an enumerator for external iteration via next that is clone-able, so that the clone retains the current enumeration state.
As an example, let's say I have a method that returns an enumerator which yields square numbers:
def square_numbers
return enum_for(__method__) unless block_given?
n = d = 1
loop do
yield n
d += 2
n += d
end
end
square_numbers.take(10)
#=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
And I want to enumerate the first 5 square numbers, and for each value, print the subsequent 3 square numbers. Something that's trivial with each_cons:
square_numbers.take(8).each_cons(4) do |a, *rest|
printf("%2d: %2d %2d %2d\n", a, *rest)
end
Output:
1: 4 9 16
4: 9 16 25
9: 16 25 36
16: 25 36 49
25: 36 49 64
But unlike the above, I want to use external iteration using two nested loops along with next and clone:
outer_enum = square_numbers
5.times do
i = outer_enum.next
printf('%2d:', i)
inner_enum = outer_enum.clone
3.times do
j = inner_enum.next
printf(' %2d', j)
end
print("\n")
end
Unfortunately, the above attempt to clone raises a:
`initialize_copy': can't copy execution context (TypeError)
I understand that Ruby doesn't provide this out-of-the-box. But how can I implement it myself? How can I create an Enumerator that supports clone?
I assume that it's a matter of implementing initialize_copy and copying the two variable values for n and d, but I don't know how or where to do it.
Ruby fibers cannot be copied, and the C implementation of Enumerator stores a pointer to a fiber which does not appear to be exposed to Ruby code in any way.
https://github.com/ruby/ruby/blob/752041ca11c7e08dd14b8efe063df06114a9660f/enumerator.c#L505
if (ptr0->fib) {
/* Fibers cannot be copied */
rb_raise(rb_eTypeError, "can't copy execution context");
}
Looking through the C source, it's apparent that Enumerators and Fibers are connected in a pretty profound way. So I doubt that there is any way to change the behavior of initialize_copy to permit clone.
Perhaps you could just write a class of your own that does what you ask:
class NumberSquarer
def initialize
#n = #d = 1
end
def next
ret = #n
#d += 2
#n += #d
ret
end
end
ns1 = NumberSquarer.new
Array.new(5) { ns1.next }
# => [1, 4, 9, 16, 25]
ns2 = ns1.clone
Array.new(5) { ns2.next }
# => [36, 49, 64, 81, 100]
Disclaimer: I'm answering my own question
One way to achieve this is by sub-classing Enumerator. In particular, the now-deprecated variant that takes an object and a method:
class ObjectEnumerator < Enumerator
attr_reader :object, :method
def initialize(object, method = :each)
#object = object
#method = method
super
end
def initialize_copy(orig)
initialize(orig.object.clone, orig.method)
end
end
That orig.object.clone above is where the magic happens: it clones the object we are traversing.
In addition, we need such clone-able object. A simple way is to have a class which holds the state as instance variables: (shamelessly copied from Kache's answer)
class NumberSquarer
def initialize
#d = -1
#n = 0
end
def each
return ObjectEnumerator.new(self, __method__) unless block_given?
loop do
#d += 2
#n += #d # had to be reordered b/c
yield #n # <- yield has to come last
end
end
end
This gives us a basic, clone-able enumerator:
e = NumberSquarer.new.each
#=> #<ObjectEnumerator: #<NumberSquarer:0x00007fde60915e10 #d=-1, #n=0>:each>
e.next #=> 1
e.next #=> 4
other = enum.clone
#=> #<ObjectEnumerator: #<NumberSquarer:0x00007fcf23842520 #d=3, #n=4>:each>
enum.next #=> 9
enum.next #=> 16
other.next #=> 9
I'm providing a different solution that is not a straight answer to the question:
How can I create an Enumerator that supports clone?
But if I'm not wrong the only purpose of cloning the not clonable Ruby's Enumerator is to get a reference to the next object in the enumerator.
In this case, we need both values stored in odd_sum and square in the example below.
We can store those values in an Array and return the array instead of a single value, then we can use Enumerator.peek in order to have the array that is used to initialize a new Enumerator.
def square_numbers(starters = {})
return enum_for(__method__, starters) unless block_given?
last_odd = starters.fetch(:square_odd, [1,1])[1]
square = starters.fetch(:square_odd, [1,1])[0]
loop do
yield [square, last_odd]
last_odd += 2
square += last_odd
end
end
outer_enum = square_numbers
5.times do
i = outer_enum.next[0]
printf('%2d:', i)
inner_enum = square_numbers(square_odd: outer_enum.peek)
3.times do
j = inner_enum.next[0]
printf(' %2d', j)
end
print("\n")
end

Ruby lazy enumerator returning different object types depending on use

I was attempting to solve Project Euler #58 in a functional manner with ruby.
Briefly, I created an enumerator to return the corner number of each ring. I was then chaining functional operators on the enumerator. When I get my result, I find that it has a different class depending on how I use it.
spiral = Enumerator.new do |yielder|
n = 3
step = 2
loop do
vals = n.step(nil, step).take(4)
yielder.yield vals
step += 2
n = vals.last + step
end
end
primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113]
levels = spiral
.lazy
.map { |ring| ring.count { |n| primes.include? n } }
.with_object({:total=>1.0, :primes=>0})
.take_while do |ring_primes, counts|
counts[:total] += 4
counts[:primes] += ring_primes
(counts[:primes] / counts[:total]) > 0.5
end
The class of levels is a lazy enumerator. I would expect it to contain the number of primes in each ring [3, 2, 3, etc.] - see the project euler reference.
If I just print from the enumerator, I get what I expect:
levels.each do |level|
puts "#{level}"
end
Returns:
3
2
3
1
But if I loop .with_index I get an array result back where the expected value is the first member and the second is my .with_object parameter
levels.each.with_index do |level, ix|
puts "#{ix}: #{level}"
end
Returns:
0: [3, {:total=>5.0, :primes=>3}]
1: [2, {:total=>9.0, :primes=>5}]
2: [3, {:total=>13.0, :primes=>8}]
3: [1, {:total=>17.0, :primes=>9}]
Why does the lazy enumerator work this way and how could I predict for it in the future?
Update
I asked around on the IRC ruby channel and no one there had any idea about it. They said they had discussed it a day or two ago and hadn't come to any conclusions.
In general, it seems one must just deal with it and move on.
What's happening here is you're conveniently ignoring the structure that's returned and plucking out the first item to display. In this case the first item is the counts structure you produce.
Have a look at this:
levels.each do |*level|
puts level.inspect
end
That shows you what's actually in the levels results. When Ruby calls a lambda it will discard any additional data that doesn't fit with the number of arguments the block accepts.
If you don't need that metadata, strip it out:
levels = spiral
.lazy
.map { |ring| ring.count { |n| primes.include? n } }
.with_object({:total=>1.0, :primes=>0})
.take_while do |ring_primes, counts|
counts[:total] += 4
counts[:primes] += ring_primes
(counts[:primes] / counts[:total]) > 0.5
end
.map { |r,_| r }
That removes the extraneous element in the results.
Here's a way of cleaning up your Enumerator a bit:
class Spiral
include Enumerable
def each
Enumerator.new do |yielder|
n = 3
step = 2
loop do
vals = n.step(nil, step).take(4)
yielder.yield vals
step += 2
n = vals.last + step
end
end
end
end
Then you can create one with:
Spiral.new.each ...

Longest palindrome within a string

I am supposed to return the size of the largest palindrome within a given string. For example, if I pass "racecar", I should get a return of 7. If I pass "racecarveryfast" or "veryfastracecar", it should still return 7. Specs I have to pass are:
Test.assert_equals(longest_palindrome("a"), 1)
Test.assert_equals(longest_palindrome("aa"), 2)
Test.assert_equals(longest_palindrome("baa"), 2)
Test.assert_equals(longest_palindrome("aab"), 2)
Test.assert_equals(longest_palindrome("baabcd"), 4)
Test.assert_equals(longest_palindrome("baablkj12345432133d"), 9)
and I am passing the first four with this code:
def longest_palindrome s
sub_count = 0
palidrome_count = []
s_array = s.chars
puts "string: " + s
puts "string array: " + s_array.to_s
if s.reverse == s
return s.size
else
s.match('(.)\1')[0].size
end
end
My thought process from here is breaking apart the string into smaller chunks, maybe through a loop. Any help or guidance would be appreciated.
def longest_palindrome(string)
i = 0
a = []
while !string[i..-1].empty?
j = -1
while !string[i..j].empty?
s = string[i..j]
if s.reverse == s
a << s.length
end
j -= 1
end
i += 1
end
a.max
end
Suppose the string has n characters. First see if the entire string is a palindrome. If it is, return the string. Fini! If not, see if either of the two substrings of length n-1 is a palindrome. If one is, return it. If not, examine substrings of length n-2, and so on. As long as the string contains at least one letter, the longest palindrome will be found.
def longest_palindrome(str)
arr = str.downcase.chars
str.length.downto(1) do |n|
ana = arr.each_cons(n).find { |b| b == b.reverse }
return ana.join if ana
end
end
The key method here is Enumerable#each_cons.
Here are some examples1:
longest_palindrome "a" #=> "a"
longest_palindrome "aa" #=> "aa"
longest_palindrome "baa" #=> "aa"
longest_palindrome "aab" #=> "aa"
longest_palindrome "baabcd" #=> "baab"
longest_palindrome "baablkj12345432133d" #=> "123454321"
longest_palindrome "I heard tattarrattats" #=> "tattarrattat"
1 James Joyce coined the word "tattarrattat" in Ulysses, to mean a knock on the door.

Ruby #select, but only select a certain number

Whats the best way in Ruby to do something like my_array.select(n){ |elem| ... }, where the n means "I only want n elements returned, and stop evaluating after that number is reached"?
This should do the trick:
my_array.select(n) { |elem| elem.meets_condition? }.take(n)
However, this will still evaluate all items.
If you have a lazy enumerator, you could do this in a more efficient manner.
https://github.com/ruby/ruby/pull/100 shows an attempt at enabling this feature.
You can easily implement lazy_select:
module Enumerable
def lazy_select
Enumerator.new do |yielder|
each do |e|
yielder.yield(e) if yield(e)
end
end
end
end
Then things like
(1..10000000000).to_enum.lazy_select{|e| e % 3 == 0}.take(3)
# => [3, 6, 9]
execute instantly.
Looks like there's no avoiding a traditional loop if you're using stock 1.8.7 or 1.9.2...
result = []
num_want = 4
i = 0
while (elem = my_array[i]) && my_array.length < num_want
result << elem if elem.some_condition
i += 1
end
You could make an Enumerable-like extension which has your desired selectn semantics:
module SelectN
def selectn(n)
out = []
each do |e|
break if n <= 0
if yield e
out << e
n -= 1
end
end
out
end
end
a = (0..9).to_a
a.select{ |e| e%3 == 0 } # [0, 3, 6, 9]
a.extend SelectN
a.selectn(1) { |e| e%3 == 0 } # [0]
a.selectn(3) { |e| e%3 == 0 } # [0, 3, 6]
# for convenience, you could inject this behavior into all Arrays
# the usual caveats about monkey-patching std library behavior applies
class Array; include SelectN; end
(0..9).to_a.selectn(2) { |e| e%3 == 0 } # [0,3]
(0..9).to_a.selectn(99) { |e| e%3 == 0 } # [0,3, 6, 9]
Why not flip this around and do the #take before the #select:
my_array.take(n).select { |elem| ... }
That will ensure you only do your computation for n number of items.
EDIT:
Enumerable::Lazy is known to be slower, but if your computation is known to be more computationally expensive than the lazy slowness, you can use the Ruby 2.0 feature:
my_array.lazy.select { |elem| ... }.take(n)
See: http://blog.railsware.com/2012/03/13/ruby-2-0-enumerablelazy/
I guess broken loop can be done in old-fashioned loop style with break or something like this:
n = 5
[1,2,3,4,5,6,7].take_while { |e| n -= 1; n >= 0 && e < 7 }
In functional language this would be recursion, but without TCO it doesn't make much sense in Ruby.
UPDATE
take_while was stupid idea as dbenhur pointed out, so I don't know anything better than a loop.

Resources