"undefined method 'zero' for Nil:Class" when #sum the Array without Nils - compile-time

The issue happens when the variable, that the array was built from, was a nil initially.
y = (1..2).map do
v = nil
v = 1
v
end
p y # => [1, 1]
p y.class # => Array(Int32)
p y.sum # => 2
When v stops being nil on a condition, that is potentially computational and not solvable while compiling:
z = (1..2).map do
v = nil
v = 1 if true
v
end
p z # [1, 1]
p z.class # => Array(Nil | Int32)
The array gets more complex type, that isn't compatible with current sum implementation, so p z.sum causes compile time error:
undefined method 'zero' for Nil:Class (compile-time type is (Nil | Int32):Class)
def sum(initial = T.zero)
^~~~
How am I supposed to fight this properly?
Or maybe it waits for some better implementation of stdlib sum method or anything else?
UPD: inject gives the same:
p z.inject{ |i, j| i + j }
undefined method '+' for Nil (compile-time type is (Nil | Int32))

You can use Iterator#compact_map to select non-nil values. The compiler will be able to infer a Array(Int32) in that case.
http://play.crystal-lang.org/#/r/e85
z = (1..2).map do
v = nil
v = 1 if true
v
end
pp typeof(z) # => Array(Nil | Int32)
pp z # => z = [1, 1]
y = z.compact_map(&.itself)
pp typeof(y) # => Array(Int32)
pp y # => y = [1, 1]
Also, notice that typeof(Expr) and Expr.class might lead to different results. The first is the compile time type and the later is the runtime type.

An alternative solution to what Brian says is to use sum with a block:
http://play.crystal-lang.org/#/r/ein
z = (1..2).map do
v = nil
v = 1 if true
v
end
puts z.sum { |x| x || 0 } #=> 2

Related

Ruby << syntax method raises an error, but .push method not when using yield. Why?

I am trying to figure out why the case C is not working. As you can see when I use 'yield' and '<<' sugar syntax it raises an error, but if I use the method's name 'acc.push' it works. In the other hand, if I use the 'result' variable to get yield result and then add to acc array using << syntax, it works. I just would like to understand why it does not work in the case C. Thanks.
Case A - Working fine
def my_map(my_arr)
c = 0 # the counter
acc = [] # new array
until c == my_arr.length
acc.push(yield my_arr[c])
c += 1
end
acc
end
p my_map( [1,2,3,4] ) { |each| each * 10 }
Case B - Working fine
def my_map(my_arr)
c = 0 # the counter
acc = [] # new array
until c == my_arr.length
result = yield my_arr[c]
acc << result
c += 1
end
acc
end
p my_map( [1,2,3,4] ) { |each| each * 10 }
Case C - Error: syntax error, unexpected local variable or method, expecting `end' acc << yield my_arr[c]
def my_map(my_arr)
c = 0 # the counter
acc = [] # new array
until c == my_arr.length
acc << yield my_arr[c]
c += 1
end
acc
end
p my_map( [1,2,3,4] ) { |each| each * 10 }
While you expect that Ruby interprets acc << yield my_arr[c] as
acc.<<(yield(my_arr[c]))
Ruby actually understands it like this
acc.<<(yield)(my_arr[c])
Which doesn't make much sense. It can be fixed by using parentheses as others already mentioned:
acc << yield(my_arr[c])
You're hitting a case of operator precedence, which Ruby can't resolve for itself.
You can fix it by using parentheses to provide enough clarity for Ruby to work out how to parse the offending line:
acc << yield(my_arr[c])
Just use parenthesis for the yield method call.
acc << yield(my_arr[c])
My guess is that the << tries to add the yield directly to the array which results in the syntax error.

For a new index from two indexes in a for loop

I was working on lua and looped over two tables and wanted to create a new table out of it, with no nil values in it. So this is basically a cross product. E.g:
{1,2,3} x {3,4,5} -> {1*3,1*4,1*5,2*3,2*4,2*5,3*3,3*4,3*5}
Of course this is not hard to do:
t = {1,2,3}
s = {3,4,5}
xs = {}
q = 1
for i,h in ipairs(t) do
for j,k in ipairs(s) do
xs[q] = h * k
q = q + 1
end
end
We keep a counter q and add 1 every iteration. And this works fine. However is it also possible without a counter? Can I fill up x so with just i and j such that there are no gaps in x?
t = {1,2,3}
s = {3,4,5}
xs = {}
for i,h in ipairs(t) do
for j,k in ipairs(s) do
q = f(i,j) -- <- I want to know if f is possible to write
xs[q] = h * k
end
end
I would say not, at least I was not able to find one myself easily.
EDIT: It is possible though if I am allowed to use the size of s.
s = {1,2,3}
t = {4,5,6}
xs = {}
for i,h in ipairs(s) do
for j,k in ipairs(t) do
q = i + (j - 1) * #t
xs[q] = h * k
end
end
You can use table.insert, there is no reason to specify the index in your case.
s = {1,2,3}
t = {4,5,6}
xs = {}
for i,h in ipairs(s) do
for j,k in ipairs(t) do
table.insert(xs, h * k)
end
end
for _, v in ipairs(xs) do
print(v)
end
Resource on insert:
https://www.lua.org/pil/19.2.html

Ruby showel operator vs (?:) conditional

I have the following code:
#ids = []
x = 'a'
#ids << x == 'a' ? [1,2] : [3,4]
#ids
I expect that in next line the #ids value should be
#ids = [1,2], but I obtain #ids = ['a']
Why ?
Operations are executed in the order of their precedence.
The operations in your relevant line of code are executed in that order:
<<
==
?, :
See the full list at Ruby's operation precedence.
Here, parenthesis indicate what actually happens in your example:
(((#ids << x) == 'a') ? [1,2] : [3,4])
^^^----1----^ ^ ^
||---------2--------| |
|------------------3-----------------|
To get the result you expected, write
#ids << (x == 'a' ? [1,2] : [3,4])
or
#ids.push(x == 'a' ? [1,2] : [3,4])
I hope you find this helpful.

Operator overloading not working

While reading Programming in Lua, I tried this example given in the book for operator overloading
Set = {}
mt = {}
mt.__add = Set.union
--create a new set with the values of the given list
function Set.new (l)
local set = {}
setmetatable (set, mt)
for _, v in ipairs (l) do
set [v] = true
end
return set
end
function Set.union (a, b)
local result = Set.new {}
for k in pairs (a) do result [k] = true end
for k in pairs (b) do result [k] = true end
return result
end
function Set.intersection (a, b)
local result = Set.new {}
for k in pairs (a) do result [k] = b[k] end
return result
end
function Set.tostring (set)
local l = {}
for e in pairs (set) do
l[#l + 1] = e
end
return "{" .. table.concat (l, ", ") .. "}"
end
function Set.print (s)
print (Set.tostring (s))
end
s1 = Set.new {10, 20, 30, 50}
s2 = Set.new {30, 1}
Set.print (s1)
Set.print (s2)
s3 = s1 + s2
Set.print (s3)
But with the latest lua for windows I am getting the following error
lua: C:\meta.lua:47: attempt to perform arithmetic on global 's1' (a table value)
stack traceback:
C:\meta.lua:47: in main chunk
[C]: ?
{30, 10, 20, 50}
{1, 30}
You are making this assignment too early:
mt.__add = Set.union
because Set.union is not initialized yet.
Move this below Set.union and it will work.
For the same reason, if you assign mt.__mul, this should be below Set.intersection
You need to define mt as a suitable metatable:
mt = { __add = Set.union, __mul = Set.intersection, __tostring = Set.tostring }

How to insert a new hash into existing hash in ruby

I have two hashes:
p = {"name"=>"TRICHI", "subdistrict"=>{"WANDIWASH"=>"1234"}}
q = {"name"=>"VELLORE", "subdistrict"=>{"WANDIWASH"=>"4183"}}
I need to make this as
r = [{"name"=>"VELLORE", "subdistrict"=>{"WANDIWASH"=>"4183"}},
{"name"=>"TRICHI", "subdistrict"=>{"WANDIWASH"=>"1234"}}]
I guess you want this:
r = [] << p << q
# or r = [p, q]
# either way you'll get:
# [ {"name"=>"VELLORE", "subdistrict"=>{"WANDIWASH"=>"4183"}},
# {"name"=>"TRICHI", "subdistrict"=>{"WANDIWASH"=>"1234"}} ]
This way you will have an array with 2 hashes.
As Tim pointed, r doesn't seem to be a Hash, maybe you meant an Array, in which case you can do
r = [p,q]
or
r = []
r << p
r << q
.. keep going for any other entry you want to push into r

Resources