Do all? and any? guarantee short-circuit evaluation? - ruby

Testing out some code in both pry and irb, I get the following results:
[1] pry(main)> a = [1, 3, 5, 7, 0]
=> [1, 3, 5, 7, 0]
[2] pry(main)> a.any? {|obj| p obj; 3 / obj > 1}
1
=> true
[3] pry(main)> a.all? {|obj| p obj; 3 / obj > 1}
1
3
=> false
In [2] and [3] I see that there appears to be short-circuit evaluation that aborts the iteration as soon as possible, but is this guaranteed behaviour? Reading the documentation there is no mention of this behaviour. I realise that I can use inject instead as that will iterate over everything, but I'm interested in finding out what the official Ruby view is.

Yes.
In the final draft of the Ruby standard, all? is defined as such:
Invoke the method each on the receiver
For each element X which the method each yeilds:
If block is given, call block with X as argument. If this call returns a falseish object, return false.
If block is not given, and X is a falseish object, return false.
Return true.
Note the word return in step 2. This guarantees short circuit evaluation. any? is defined similarly. However the standard is still a draft and I don't know which Ruby implementations (if any) aim to be standards-compliant.

The any? method just realizes the 'or' logic function over the Enumerable. It could be interpreted as statement:
y = x1 v x2 v x3 v ... v xn
And the all? method realizes 'and' logic function over the Enumerable. It also could be interpreted as statement:
y = x1 * x2 * x3 * ... * xn
Since the Array is an Enumerable, it also includes those methods. So, for the method any? the first occurience of true (exactly neither nil nor false) result breaks enumeration with true result. In example the yielded becomes true on number 4, so the methods breaks the execution the returns true:
[1,2,3,4,5].any? {| x | puts x ; x > 3 }
# 1
# 2
# 3
# 4
# => true
Also you can apply DeMorgan's rule to the function any?, and to use all? method:
![1,2,3,4,5].all? {| x | puts x ; x <= 3 }
# 1
# 2
# 3
# 4
# => true
For the method all? the first occurience of either false or nil result do something similar, i.e. returns false. In example the yielded becomes false on number 3, so the methods breaks the execution the returns false:
[1,2,3,4,5].all? {| x | puts x ; x < 3 }
# 1
# 2
# 3
# => false
And with DeMorgan's transformation to use any? method:
![1,2,3,4,5].any? {| x | puts x ; x >= 3 }
# 1
# 2
# 3
# => false

I think there is some ambiguity here.
Consider the following:
RUBY_VERSION
=> "2.3.7"
When yielding to a block:
[1,2,3,4,5].all? {| x | puts x ; x < 3 }
# 1
# 2
# 3
# => false
When not yielding to a block:
def a
puts "in a"
true
end
def b
puts "in b"
false
end
def c
puts "in c"
true
end
[a,b,c].all?
# in a
# in b
# in c
# => false
Seems like condition #2:
If block is not given, and X is a falseish object, return false.
Is not valid.

Related

What's Python's next() in Ruby?

I am currently learning Ruby and am lost a bit.
This Python snippet:
n = iter([1,2,3,4,5])
for x in n:
print(x)
y = next(n)
print(y)
gives:
1
2
3
4
5
And I'm trying to do the same in Ruby, which is not working:
n = [1,2,3,4,5].each
for x in n do
puts x
y = n.next()
puts y
end
How do I need to write the Python example in Ruby?
You have the right idea except Ruby keeps track of the position of the enumerator internally. Try this:
enum = [1,2,3,4,5].each
#=> #<Enumerator: [1, 2, 3, 4, 5]:each>
loop do
puts enum.next
puts enum.next
end
1
2
3
4
5
Enumerator#next raises a StopInteration exception when invoked after all elements of the enumerator enum have been generated. Kernel#loop handles the exception by breaking out of the loop.
Note that Array#each, without the optional block, has the same effect as Object#to_enum. Also, though authors of Ruby books feel an obligation to cover for loops (seemingly, on page one), they are never used in practice.
In Python the iterator n obtained applying the function iter() can be consumed once.
array = [1,2,3,4,5]
n = iter(array)
for x in n:
print(x)
# 1
# 2
# 3
# 4
# 5
If you then call next(n) you get no output but error: StopIteration. If you try to iterate n again you get no output (no error, the for loop hanldes the exception).
But you can iterate over the array:
for x in array:
print(x)
If you check the output a code like yours, you get:
n = iter([1,2,3,4,5])
for x in n:
print('odd_', x)
y = next(n)
print('even', y)
# odd_ 1
# even 2
# odd_ 3
# even 4
# odd_ 5
# StopIteration
As pointed by #toniedzwiedz Object#to_enum is the equivalent for the Python iter(), so you can call Enumerator#next on it.
In Ruby you can consume more than once the enumerator:
array = [1, 2, 3, 4, 5]
n = array.to_enum
# n = array.each # each returns an Enumerable
for x in n do
puts x
end
for x in n do
puts x
end
Since you can consume more than once the enumerator you get "double" output:
for x in n do
p x
p n.next
end
Or using Array#each:
n.each do |x| # same as array.each.each
p x
p n.next
end
Also in Ruby you reach end the iteration signal:
6.times do
p n.next
end
# `next': iteration reached an end (StopIteration)
You're just trying to print each element of the Array?
[1,2,3,4,5].each {|n| puts n}
If you're looking for a direct equivalent of Python's iterator, you should check out Enumerator#next for a very similar API
arr = [1, 2, 3].to_enum
#<Enumerator: [1, 2, 3]:each>
arr.next
# 1
for x in arr do
puts x
puts arr.next
end
#1
#2
#2
#3
#3
#StopIteration: iteration reached an end
each_cons is what you need. But this method has a downside that is the current will not print last element of the array. Please keep in mind this.
[1,2,3,4,5].each_cons(2) { |current,next_val| puts next_val }

Ruby method with parameters in double parenthesis

I have come across the following code in erb library. Note the double (( )):
class MyTest
def location=((filename, lineno))
#filename = filename
#lineno = lineno if lineno
end
end
The following locatia= method is another version without the (( )) for testing:
class MyTest
def locatia=(filename, lineno)
#filename = filename
#lineno = lineno if lineno
end
end
I got this result:
a = MyTest.new
a.location = "foo", 34
a # => #<MyTest:0x2a2e428 #filename="foo", #lineno=34>
b = MyTest.new
b.location = "foo"
b # => #<MyTest:0x2a2e338 #filename="foo">
c = MyTest.new
c.locatia = "foo", 34
c # >> `locatia=': wrong number of arguments (given 1, expected 2) (ArgumentError)
The version with double parenthesis works fine. The one with single fails. It must be specified on some level of the source code. Any clues?
Destructuring.
location= accepts one parameter. This parameter is assumed to be an array, and is destructured so that the first element of array goes into filename, and the second into lineno. The outer parentheses are the normal (usually optional) parentheses of a method definition; the inner parentheses indicate the structure of the first (and only) parameter.
Here's another example of destructuring at work:
{ foo: 17, bar: 34 }.each.with_index { |(key, value), index|
p [key, value, index]
}
# => [:foo, 17, 0]
# [:bar, 34, 1]
Hash#each generates pairs [key, value]; Enumerator#with_index generates pairs of [value, index]. Apply them both, and you get [[key, value], index] passed to the block. We could just do this:
{ foo: 17, bar: 34 }.each.with_index { |pair, index|
key = pair[0]
value = pair[1]
p [key, value, index]
}
but it is so much simpler with destructuring. We could even write (key, value) = pair (or key, value = pair, as single-rvalue arrays are automatically destructured in multi-lvalue assignment) as another example of destructuring.
It's a little unusual to see this in production code, but what's happening here is list expansion in an argument list:
def location=((filename, lineno))
end
What this means is you call it this way:
x.location = 1,2
Where those get expanded into two separate arguments. A mutator method can only take one argument, but that argument can be a list and you can expand that argument into multiple values.
Normally you see this in iterators like:
{ a: 'b', c: 'd' }.each_with_index.map do |(k,v), i|
# k, v come in as a pair, i is separate
end
Though even then it's pretty rare.
You can also see it in other cases:
a = [ 1, 2 ]
b = 3
# Without list expansion, just one-to-one assignment
x, y, z = a, b
# x => [ 1, 2 ]
# y => 3
# z => nil
# With list expansion
(x, y), z = a, b
# x => 1
# y => 2
# z => 3

Ruby map! does not change contained variable

Suppose I have an object x, and an array y=[x] which contains x. If I manipulate x, then y does not change:
x = 1 # => 1
y = [x] # => [1]
x = x+1 # => 2
x # => 2
y # => [1]
and if I change y, then x does not change.
x = 1 # => 1
y = [x] # => [1]
y.map!{|a| a+1} # => [2]
y # => [2]
x # => 1
Is there a way to have them change in parallel? It feels like when I map! over an array the underlying values should change.
first of all
x = x + 1
will create a new variable with an old name x
x = 1
y = [x]
x.object_id
# 3
y[0].object_id
# 3
x = x + 1
x.object_id
# 5
y[0].object_id
# 3
Second, numbers are immutable objects in Ruby (and Strings, for example, are mutable). So you just can't do what you want using number objects in Ruby. What you can do is slightly more obscure. You can create your own mutable object (container for a number) and use a reference to this object in your array.
class MutableNumber
attr_accessor :n
def initialize(n)
#n = n
end
end
x = MutableNumber.new(1)
y = [x]
y[0].n
#=> 1
x.n += 1
y[0].n
#=> 2
You can go little bit further and to add more magic here to mimic numbers
class MutableNumber
attr_accessor :n
def initialize(n)
#n = n
end
def method_missing(m, *args, &blk)
#n = #n.send(m, *args, &blk)
self
end
end
x = MutableNumber.new(1)
y = [x]
y[0].n
#=> 1
x += 1
y[0].n
#=> 2
But I would not encourage you to do any of this.
Just stick to the idea that numbers are immutable. And overall you should be careful with mutability.
map! is mutable for the array, not for its elements. It means that the array gets new values in place (i.e. it's assigned to original array). Elements inside the array are not mutated, but replaced by new elements.
If you want to change old values, you can iterate using each and call mutating methods on each element. You can see this with array of strings:
a = "a"; b = "b"; aa = [a, b]
#=> ["a", "b"]
aa.map!{|e| e.capitalize }
#=> ["A", "B"]
[a, b]
#=> ["a", "b"]
a = "a"; b = "b"; aa = [a, b]
#=> ["a", "b"]
aa.each{|e| e.capitalize! }
#=> ["A", "B"]
[a, b]
#=> ["A", "B"]
Note that it won't work for immutable objects and numbers are immutable, as #fl00r explained in his answer.

Variable changing value, ruby

I am not sure how this variable called origString is changing value in my loop
def scramble_string(string, positions)
i = 0
origString = string
puts origString
newString = string
while i < string.length
newString[i] = origString[positions[i]]
i = i + 1
end
puts origString
return newString
end
for example if I run scramble_string("abcd", [3, 1, 2, 0])
origString changes from "abcd" in the first "puts" to "dbcd" in the second one.
How am I changing the value of origString if I am only declaring it once?
When you say x = y in Ruby that creates a variable with a reference to exactly the same object. Any modifications to x will apply to y and vice-versa:
y = "test"
x = y
x[0] = "b"
x
# => "best"
y
# => "best"
You can tell because of this:
x.object_id == y.object_id
# => true
They're identical objects. What you want is to make a copy first:
x = y.dup
x[0] = "b"
x
# => "best"
y
# => "test"
This results in two independent objects:
x.object_id == y.object_id
# => false
So in your case what you need is to change it like:
orig_string = string.dup
Now that being said, often the best way to process things in Ruby is by using functions that return copies, not manipulating things in place. A better solution is this:
def scramble_string(string, positions)
(0...string.length).map do |p|
string[positions[p]]
end.join
end
scramble_string("abcd", [3, 1, 2, 0])
"dbca"
Note that's a lot more succinct than the version with string manipulation.

ruby regular expression begin method a bit confusing

m = /(.)(.)(\d+)(\d)/.match("THX1138.")
puts m[0]
c = m.captures #=> HX1138
puts c[0] #=> H
puts m.begin(0) #=> 1
puts c[1] #=> X
puts m.begin(1) #=> 1
puts c[2] #=> 113
puts m.begin(2) #=> 2
I was expecting m.begin(1) to return 2 since X is two elements after the beginning of string.
I am reading the book well grounded rubyist which says
To get the information for capture n,
you provide n as the argument to begin
and/or end.
Similarly I was expecing m.begin(2) to rerturn 3.
Read carefully:
Returns the offset of the start of the nth element of the match array in the string.
So the match array is actually [HX1138,H,X,113,8]
SO
m.begin(0) => offset of HX1138 => 1 in "THX1138"
m.begin(1) => offset of H => 1 in "THX1138"
m.begin(2) => offset of X => 2 in "THX1138"

Resources