2

I'm trying to make a program in Ruby that prints text out and pauses both for every full stop "." or "..." in a string. I've got this so far but don't know where to take it next.

def talk(text, speed = 0.008)
    pause = speed * 100
    text.each_char do |char| 
        print char
        if char == '.'
            sleep(pause)
        end
        sleep(speed) 
    end
    return
end

# e.g. talk("I can see something... It's getting closer... Oh no.")

It pauses fine for one full stop but obviously doesn't work for three in a row as it just repeats the pause three times. I'm more of a beginner and struggling to find a solution. Thanks for reading

2 Answers2

1

Here's my suggestion -- print all the .s as they appear, sleeping before printing the next character.

def talk(text, speed = 0.008)
    pause = speed * 100
    last_char = nil # keep track of the previous character.

    text.each_char do |char|
        if char == '.' 
            # no sleep at all for periods
            print('.')
        else
            # we're going to print something, if the last character was a period,
            # pause before doing it.  
            sleep(pause) if last_char == '.'
            print char
            # finally, a regular sleep 
            sleep(speed)
        end
        # and update the last character we saw
        last_char = char
    end
end

Of course, there's lots of different ways to solve this problem!

John Ledbetter
  • 13,557
  • 1
  • 61
  • 80
1
def talk(text, speed = 0.008)
  pause = speed * 100
  r = /(\.{3}|\.)/
  text.split(r).each do |str|
    if str.match?(r)
      sleep(pause)
    else
      print str
    end
  end
  puts
end
talk("Mary had .a lit...tle .lamb", 0.04)
  #=> Mary had a little lamb
              ^     ^      ^

A four-second delay occurred after each character indicated by a party hat was printed.

The steps are as follows.

text = "Mary had .a lit...tle .lamb"
speed = 0.04
pause = speed * 100
r = /(\.{3}|\.)/

This regular expression reads, "match three periods or (|) one period in capture group 1" (the capture group being defined by the parentheses)". This is not the same as /(\.|\.{3})/, which would match each period of each group of three individually (that is, \. before | would succeed for every period, part of a group or not).

a = text.split((r))
  #=> ["Mary had ", ".", "a lit", "...", "tle ", ".", "lamb"]

See String#split, which explains the need for the capture group, to include the splitting strings in the array returned.

Continuing,

a.each do |str|
  if str.match?(r)
    sleep(pause)
  else
    print str
  end
end

This prints (without a newline character) "Mary had ", sleeps for four seconds, prints "a lit", sleeps for another four seconds, and so on.

puts

This last statement is needed to print a newline character to terminate the line.


Another way is to make a few small change to the method given in the question.

def talk(text, speed = 0.008)
  pause = speed * 100
  text.squeeze('.').each_char do |char| 
    if char == '.'
      sleep(pause)
    else
      print char
    end
  end
  puts
end

See String#squeeze which converts each string of two or more periods to one period.

This method differs from the earlier one in its treatment of two or more than three periods in a row.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100