What's the difference between:
x = 1
while x < 5 do
x += 1
print x
end
and:
x = 1
while x < 5
x += 1
print x
end
whereby do is added to the first one? Or, is there difference?
There is no difference when you include the newline between the while loop declaration and the code. So:
while flag
#code here
end
is the same as:
while flag do
#code here
end
However, when you include do, you can put code on the same line, as in:
while flag do myMethod() end
It is just used to separate the actual loop code.
A while loop's conditional is separated from code by the reserved word
do, a newline, backslash \, or a semicolon ;.
https://www.tutorialspoint.com/ruby/ruby_loops.htm
Related
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
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!
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.
I am expecting to print each number, not just one big value. This is my code:
i = 20
loop {
i -= 1
print i
break if i <= 0
}
# >> 191817161514131211109876543210
I have tried:
printing just i
switching braces with do and end
changing the value of i
puts adds a new line after each argument passed to it (unless there's already a newline ending the arg).
print does not add a new line.
Therefore, you need to switch to puts:
i = 20
loop {
i -= 1
puts i
break if i <= 0
}
This is roughly equivalent to using the following code:
i = 20
loop {
i -= 1
print "i\n"
break if i <= 0
}
\n is the newline character:
\n Insert a newline in the text at this point.
Here's an example of passing multiple args to a puts statement, to really simplify your code:
puts *(0..20)
Or, in the reverse order you're using:
puts *19.downto(0)
This uses the splat operator to send the range as arguments to puts, giving you the output you're after,
Hope this helps - let me know if you've any questions.
You are using print which will print lines to the console without the \n at the end.
change print to puts.
as you already got your solution, you need to replace print with puts. I am showing you an alternate solution to get the same output:
puts (0..20).to_a.reverse
OR
(0..20).to_a.reverse.each{|i| puts i}
Output:
20
19
18
17
16
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
I have the following as part of a class
def to_s
i = 0
first_line? = true
output = ''
#selections.each do | selection |
i += 1
if first_line?
output << selection.to_s(first_line?)
first_line? = false
else
output << selection.to_s
end
if i >= 5
output << "\r"
i = 0
else (output << " $ ")
end
end
return output
end
And i am getting the following syntax errors
SyntaxError: list2sel.rb:45: syntax error, unexpected '='
first_line? = true
^
list2sel.rb:47: syntax error, unexpected keyword_do_block, expecting keyword_end
#selections.each do | selection |
^
list2sel.rb:51: syntax error, unexpected '='
first_line? = false
^
What give, also thanks in advance, this is driving me nuts.
I suppose, you can't name variables with '?' at the end.
Variable names (with a few exceptions noted below) can only contain letters, numbers and the underscore. (Also, they must begin with a letter or the underscore; they can't begin with a number.) You can't use ? or ! in a variable name.
Beyond that rule, there is a strong convention in Ruby that a question mark at the end of something indicates a method that returns a boolean value:
4.nil? # => returns false....
So even if you could use it, a variable like first_line? would confuse (and then annoy) the hell out of Rubyists. They would expect it be a method testing whether something was the first line of something (whatever exactly that means in context).
Exceptions about variable names:
Global variables begin with $ - e.g., $stdin for standard input.
Instance variables begin with # - e.g. #name for an object
Class variables begin with ## - e.g. ##total for a class
I believe this is a more concise way of doing the above (untested):
def to_s
output = ""
#selections.each_with_index do | selection,line |
output << line==0 ? selection.to_s(true) and next : selection.to_s
output << line % 5 ? " $ " : "\r"
end
return output
end
If you are not a fan of the ternary operator (x ? y : z) then you can make them ifs:
def to_s
output = ""
#selections.each_with_index do | selection,line |
if line==0
output << selection.to_s(true)
else
output << selection.to_s
if line % 5
output << " $ "
else
output << "\r"
end
end
end
return output
end
Variable names allow non-ASCII letters, and there are non-ASCII versions of the question mark, so you can put question marks (and also some forms of space characters) into variable names.
I'm looking for a much more idiomatic way to do the following little ruby script.
File.open("channels.xml").each do |line|
if line.match('(mms:\/\/{1}[a-zA-Z\.\d\/\w-]+)')
puts line.match('(mms:\/\/{1}[a-zA-Z\.\d\/\w-]+)')
end
end
Thanks in advance for any suggestions.
The original:
File.open("channels.xml").each do |line|
if line.match('(mms:\/\/{1}[a-zA-Z\.\d\/\w-]+)')
puts line.match('(mms:\/\/{1}[a-zA-Z\.\d\/\w-]+)')
end
end
can be changed into this:
m = nil
open("channels.xml").each do |line|
puts m if m = line.match(%r|(mms://{1}[\w\./-]+)|)
end
File.open can be changed to just open.
if XYZ
puts XYZ
end
can be changed to puts x if x = XYZ as long as x has occurred at some place in the current scope before the if statement.
The Regexp '(mms:\/\/{1}[a-zA-Z\.\d\/\w-]+)' can be refactored a little bit. Using the %rXX notation, you can create regular expressions without the need for so many backslashes, where X is any matching character, such as ( and ) or in the example above, | |.
This character class [a-zA-Z\.\d\/\w-] (read: A to Z, case insensitive, the period character, 0 to 9, a forward slash, any word character, or a dash) is a little redundant. \w denotes "word characters", i.e. A-Za-z0-9 and underscore. Since you specify \w as a positive match, A-Za-z and \d are redundant.
Using those 2 cleanups, the Regexp can be changed into this: %r|(mms://{1}[\w\./-]+)|
If you'd like to avoid the weird m = nil scoping sorcery, this will also work, but is less idiomatic:
open("channels.xml").each do |line|
m = line.match(%r|(mms://{1}[\w\./-]+)|) and puts m
end
or the longer, but more readable version:
open("channels.xml").each do |line|
if m = line.match(%r|(mms://{1}[\w\./-]+)|)
puts m
end
end
One very easy to read approach is just to store the result of the match, then only print if there's a match:
File.open("channels.xml").each do |line|
m = line.match('(mms:\/\/{1}[a-zA-Z\.\d\/\w-]+)')
puts m if m
end
If you want to start getting clever (and have less-readable code), use $& which is the global variable that receives the match variable:
File.open("channels.xml").each do |line|
puts $& if line.match('(mms:\/\/{1}[a-zA-Z\.\d\/\w-]+)')
end
Personally, I would probably just use the POSIX grep command. But there is Enumerable#grep in Ruby, too:
puts File.readlines('channels.xml').grep(%r|mms://{1}[\w\./-]+|)
Alternatively, you could use some of Ruby's file and line processing magic that it inherited from Perl. If you pass the -p flag to the Ruby interpreter, it will assume that the script you pass in is wrapped with while gets; ...; end and at the end of each loop it will print the current line. You can then use the $_ special variable to access the current line and use the next keyword to skip iteration of the loop if you don't want the line printed:
ruby -pe 'next unless $_ =~ %r|mms://{1}[\w\./-]+|' channels.xml
Basically,
ruby -pe 'next unless $_ =~ /re/' file
is equivalent to
grep -E re file
One\n
Two\n
Three\n
Four\n
remove_lines(2) would remove the first two lines, leaving the string:
Three\n
Four\n
s.to_a[2..-1].join
>> s = "One\nTwo\nThree\nFour\n"
=> "One\nTwo\nThree\nFour\n"
>> s.to_a[2..-1].join
=> "Three\nFour\n"
s = "One\nTwo\nThree\nFour"
lines = s.lines
> ["One\n", "Two\n", "Three\n", "Four"]
remaining_lines = lines[2..-1]
> ["Three\n", "Four"]
remaining_lines.join
> "Three\nFour"
String#lines converts the string into an array of lines (retaining the new line character at the end of each string)
[2..-1] specifies the range of lines to return, in this case the third through the last
Array#join concatenates the lines back together, without any space (but since the lines still contain the new line character, we don't need a separator)
In one line:
s.lines[2..-1].join
class String
def remove_lines(i)
split("\n")[i..-1].join("\n")
end
end
Calling "One\nTwo\nThree\nFour\n".remove_lines(2) would result in "Three\nFour". If you need the trailing "\n" you need to extend this method accordingly.
I had a situation where I needed to support multiple platform EOLN (both \r and \n), and had success with the following:
split(/\r\n|\r|\n/, 2).last
Or the equivalent remove_lines:
def remove_lines(number_of_lines=1)
split(/\r\n|\r|\n/, number_of_lines+1).last
end
Here is a pure regexp one-liner. Hypothetically it should be even faster than the elegant solution provided by #DigitalRoss:
n = 4 # number of lines
str.gsub(/\A(.*\n){#{n}}/,'')
If you know in advance how many line you want to cut (4 here):
str.gsub(/\A(.*\n){4}/,'')
And if you want to cut only one line:
str.gsub(/\A.*\n/,'')
In order to cut n lines from the tail:
gsub(/(\n.*){#{n}}\Z/,'')
This problem will remove the first two lines using regular expression.
Text = "One\nTwo\nThree\nFour"
Text = Text.gsub /^(?:[^\n]*\n){2}/, ''
# -----------------------------------^^ (2) Replace with nothing
# ----------------^^^^^^^^^^^^^^^^ (1) Detect first 2 lines
puts Text
EDIT: I've just saw that the question is also about 'n' lines not just two lines.
So here is my new answer.
Lines_Removed = 2
Original_Text = "One\nTwo\nThree\nFour"
Result___Text = (Original_Text.gsub(Regexp.new("([^\n]*\n){%s}" % Lines_Removed), ''))
# ^^^^^^^^^^^^^^ ^^
# - (1) Detect first lines -----++++++++++++++ ||
# - (2) Replace with nothing -----------------------------------------------------++
puts Result___Text # Returns "Three\nFour"
def remove_lines(str, n)
res = ""
arr = str.split("\n")[n..(str.size-n)]
arr.each { |i| res.concat(i + "\n") }
return res
end
a = "1\n2\n3\n4\n"
b = remove_lines(a, 2)
print b