Can someone explain Ruby's use of pipe characters in a block?
Braces define an anonymous function, called a block. Tokens between the pipe are the arguments of this block. The number of arguments required depends on how the block is used. Each time the block is evaluated, the method requiring the block will pass a value based on the object calling it.
It's the same as defining a method, only it's not stored beyond the method that accepts a block.
For example:
def my_print(i)
puts i
end
will do the same as this when executed:
{|i| puts i}
the only difference is the block is defined on the fly and not stored.
Example 2:
The following statements are equivalent
25.times &method(:my_print)
25.times {|i| puts i}
We use anonymous blocks because the majority of functions passed as a block are usually specific to your situation and not worth defining for reuse.
So what happens when a method accepts a block? That depends on the method. Methods that accept a block will call it by passing values from their calling object in a well defined manner. What's returned depends on the method requiring the block.
For example: In 25.times {|i| puts i}
.times calls the block once for each value between 0 and the value of its caller, passing the value into the block as the temporary variable i. Times returns the value of the calling object. In this case 25.
Let's look at method that accepts a block with two arguments.
{:key1 => "value1", :key2 => "value2"}.each {|key,value|
puts "This key is: #{key}. Its value is #{value}"
}
In this case each calls the block ones for each key/value pair passing the key as the first argument and the value as the second argument.
What are those pipe symbols for in Ruby?
They are the variables yielded to the block.
def this_method_takes_a_block
yield(5)
end
this_method_takes_a_block do |num|
puts num
end
Which outputs "5". A more arcane example:
def this_silly_method_too(num)
yield(num + 5)
end
this_silly_method_too(3) do |wtf|
puts wtf + 1
end
The output is "9".
what does the vertical bar character do at the beginning of a method definition's name
'"|" being used at the beginning of a method definition' indeed defines the method |
.
Syntax Error in Ruby, Unexpected Pipe Character in a Do
while (x >= 4_000_000)
foo
end
You don't even have to pass in x
, because it's accessible in the scope of the enclosing block.
Regex to match pipes not within brackets or braces with nested blocks
The following is a pure-Ruby solution. I assume the braces and brackets in the string are balanced.
str =<<BITTER_END
Some infobox royalty|testing
| name = Louis
| title = Prince Napoléon
| elevation_imperial_note= <ref name="usgs">{{cite web|url={{Gnis3|1802764}}|title=USGS}}</ref>
| a = [[AA|aa]] | b = {{cite
|title=TITLE
|author=AUTHOR}}
BITTER_END
stack = []
last = 0
str.each_char.with_index.with_object([]) do |(c,i),locs|
puts "c=#{c}, i=#{i}, locs=#{locs}, stack=#{stack}"
case c
when ']', '}'
puts " pop #{c} from stack"
stack.pop
when '[', '{'
puts " push #{c} onto stack"
stack << c
when '|'
puts stack.empty? ? " record location of #{c}" : " skip | as stack is non-empty"
locs << i if stack.empty?
end
puts " after: locs=#{locs}, stack=#{stack}"
end.map do |i|
old_last = last
last = i+1
str[old_last..i-1].strip if i > 0
end.tap { |a| a << str[last..-1].strip if last < str.size }
#=> ["Some infobox royalty",
# "testing",
# "name = Louis",
# "title = Prince Napoléon",
# "elevation_imperial_note= <ref name=\"usgs\">
# {{cite web|url={{Gnis3|1802764}}|title=USGS}}</ref>",
# "a = [[AA|aa]]",
# "b = {{cite\n|title=TITLE\n|author=AUTHOR}}"]
Note that, to improve readability, I've broken the string that is the antepenultimate element of the returned array1.
Explanation
For an explanation of how the locations of the pipe symbols on which to split are determined, run the Heredoc above to determine str
(the Heredoc needs to be un-indented first), then run the following code. All will be revealed. (The output is long, so focus on changes to the arrays locs
and stack
.)
stack = []
str.each_char.with_index.with_object([]) do |(c,i),locs|
puts "c=#{c}, i=#{i}, locs=#{locs}, stack=#{stack}"
case c
when ']', '}'
puts " pop #{c} from stack"
stack.pop
when '[', '{'
puts " push #{c} onto stack"
stack << c
when '|'
puts stack.empty? ? " record location of #{c}" : " skip | as stack is non-empty"
locs << i if stack.empty?
end
puts " after: locs=#{locs}, stack=#{stack}"
end
#=> [20, 29, 44, 71, 167, 183]
If desired, one can confirm the braces and brackets are balanced as follows.
def balanced?(str)
h = { '}'=>'{', ']'=>'[' }
stack = []
str.each_char do |c|
case c
when '[', '{'
stack << c
when ']', '}'
stack.last == h[c] ? (stack.pop) : (return false)
end
end
stack.empty?
end
balanced?(str)
#=> true
balanced?("[[{]}]")
#=> false
1 ...and, in the interest of transparency, to have the opportunity to use a certain word.
Related Topics
Rake VS. Thor for Automation Scripts
Find or Create Record Through Factory_Girl Association
Why Do I Need to Work Harder to Make My Rails Application Fit into a Restful Architecture
Can Someone Explain Ruby's Use of Pipe Characters in a Block
What Is the Use of "#!/Usr/Local/Bin/Ruby -W" at the Start of a Ruby Program
How to Call a Super Class Method
What's the Best Way to Deploy a Jruby on Rails Application to Tomcat
How to Make Nokogiri Not to Convert &Nbsp; to Space
How to Login a User with Devise
How to Handle Constants in Ruby When Using Rails
Rails Bundler Doesn't Install Gems Inside a Group
Ruby Way to Generate a Hmac-Sha1 Signature for Oauth
Setting Up the Logger in Rails 3