how to write switch statement to identity Array Object in Crystal lang - switch-statement

I want to transpose only 2d Array in Crystal lang, not 1d array.
So I write switch statement below.
a = [[1,2,3],[11,12,13]]
b = [1,2,3]
class Array
def meow
case self.first
when Array
puts self.transpose
else
puts "OK"
end
end
end
a.meow
b.meow
This code does not work. How can I fix it?
Here is the error message.
Error in foo.cr:16: instantiating 'Array(Int32)#meow()'
b.meow
^~~~
in foo.cr:8: instantiating 'Array(Int32)#transpose()'
puts self.transpose
^~~~~~~~~
in /usr/share/crystal/src/array.cr:1642: undefined method 'first' for Int32
return Array(Array(typeof(first.first))).new if empty?
^~~~~

First of all, it is usually not recommended to monkey patch types from the standard library.
The problem can be solved relatively easy by assigning the array as an argument to the method and restricting the type to Array(Array). Don't need to add specifics for any sub-types.
def meow(array : Array(Array))
array.transpose
end
meow [[1, 2, 3], [11, 12, 13]] # => [[1, 11], [2, 12], [3, 13]]
meow [[1, 2, 3], ["a", "b", "c"]] # => [[1, "a"], [2, "b"], [3, "c"]]
You probably won't need a method accepting 1-dimensional arrays, so meow [1, 2, 3] will result in a compiler error.
It's possible to add an overload like this, though:
def meow(array : Array)
"OK"
end

One of solutions could be this:
a = [[1, 2, 3], [11, 12, 13]]
b = [1, 2, 3]
class Array
def meow
case self
when Array(Array(Int32))
puts self.transpose
else
puts "OK"
end
end
end
a.meow
b.meow

Related

Are there any difference between Y and *Y, where Y is a list used as input argument?

I was using the torch.tensor.repeat()
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
period = x.size(1)
repeats = [1,2]
result = x.repeat(*repeats)
the result is
tensor([[1, 2, 3, 1, 2, 3],
[4, 5, 6, 4, 5, 6]])
if I get result as follows
result = x.repeat(repeats)
the result is the same
tensor([[1, 2, 3, 1, 2, 3],
[4, 5, 6, 4, 5, 6]])
It seems that x.repeat(repeats) and x.repeat(*repeats) work the same.
Does it mean that, for an input parameter, e.g, Y, I can use either Y or *Y
Kinda. If repeats is a (list or tuple) of ints, then it is equivalent. But in general the rule appears to be:
If the first argument is a list or tuple, take that as repeats. Ignore all other arguments.
Otherwise, take the full *args as repeats
So if your repeats is something weird like repeats=((1,2),(3,4)), then a.repeat(*repeats) succeeds and is the same as a.repeat(1, 2)==a.repeat((1, 2)) and a.repeat(repeats) fails.
Note: This is observational based on my own tests. The only official documentation is the defined type of the function, e.g. repeat(torch.Size or int...) which isn't perfectly clear with regards to semantics.
You can also get error messages like this:
TypeError: repeat(): argument 'repeats' (position 1) must be tuple of ints, not tuple
when you pass floats. In general error reporting could be better.

Sort by value size()

I need to sort by source value size() descending:
def source =
[(firstString): [3, 2, 1],
(secondString): [3, 2, 1, 4],
(thirdString): [3]]
expected:
[(secondString): [3, 2, 1, 4],
(firstString): [3, 2, 1],
(thirdString): [3]]
I've tried to sort doing this:
source.sort { -it.value.size() }
How can I achieve this?
The following is the working code for your expected result:
def source = [
"(firstString)": [3, 2, 1],
"(secondString)": [3, 2, 1, 4],
"(thirdString)": [3]
]
def sortedResult = source.sort { -it.value.size()}
println sortedResult
Working example here on groovy console : https://groovyconsole.appspot.com/script/5104124974596096
The sort that takes a Closure as an argument does not mutate the original Map. It only returns a new map, so you need to assign it (you can assign it to itself).
source = source.sort { -it.value.size() }
With Collections, there is another type of sort that takes a Boolean as well as a Closure. In this case, the Boolean indicates whether you want to mutate the original Collection or just return a new Collection.
a = [1,3,2]
a.sort (true) { it }
assert a = [1,2,3]
This doesn't apply to Map. So use the assignment syntax above.

For loop variable initialisation from a list

Groovy allows unfolding lists in assignment, as in:
(x, y) = [1, 2]
So I assumed something similar would work in a for loop, as in:
list = [[1, 2], [2, 4], [3, 6]]
for ((elm1, elm2) in list) {...}
Which turns out to be a syntax error. Is this style not possible or is there some trick to it I'm missing?
I guess it won't work with the for loop (or I definitely don't know the syntax), however a two-argument closure can be used to iterate such a List, unfold the tuples:
def list = [[1, 2], [2, 4], [3, 6]]
assert list.collect { a, b -> a + b } == [3, 6, 9, ]

Summing two 2-D lists

so this has kind of stumped me. I feel like it should be an easy problem though.
Lets say I have these two lists
a = [[3, 4], [4, 5]]
b = [[1, 2], [4, 6]]
I am trying so it would return the sum of the two 2-D lists of each corresponding element like so
c = [[4, 6], [8, 11]]
I am pretty sure I am getting lost in loops. I am only trying to use nested loops to produce an answer, any suggestions? I'm trying several different things so my code is not exactly complete or set in stone and will probably change by the time someone reponds so I won't leave a code here. I am trying though!
You could try some variation on nested for-loops using enumerate (which will give you the appropriate indices for comparison to some other 2d array):
a = [[3, 4], [4, 5]]
b = [[1, 2], [4, 6]]
Edit: I didn't see you wanted to populate a new list, so I put that in there:
>>> c = []
>>> for val, item in enumerate(a):
newvals = []
for itemval, insideitem in enumerate(item):
newvals.append(insideitem + b[val][itemval])
c.append(newvals)
newvals = []
Result:
>>> c
[[4, 6], [8, 11]]
Use numpy:
import numpy as np
a = [[3, 4], [4, 5]]
b = [[1, 2], [4, 6]]
c = np.array((a,b))
np.sum(c, axis=0)
I know it is an old question, but following nested loops code works exactly as desired by OP:
sumlist = []
for i, aa in enumerate(a):
for j, bb in enumerate(b):
if i == j:
templist = []
for k in range(2):
templist.append(aa[k]+bb[k])
sumlist.append(templist)
templist = []
print(sumlist)
Output:
[[4, 6], [8, 11]]

Groovy List Conversion

I'm having an issue in groovy trying to figure out how to convert a single item to a list. I have an incoming variable params.contacts, which could be a single value (e.g. 14) or it could be an array of values (e.g. 14, 15). I want to always turn it into a list. Previously, I was simply saying params.contacts.toList(), but this code fails when it's a single item. It would take a value of 14 and divide it into a list of [1, 4].
Is there a simple, elegant way of handling this problem?
One easy way, put it in a list and flatten it:
def asList(orig) {
return [orig].flatten()
}
assert [1, 2, 3, 4] == asList([1, 2, 3, 4])
assert ["foo"] == asList("foo")
assert [1] == asList(1)
One problem with this is that it'll completely flatten things, so it's not a good approach as it'll flatten lists within your list:
assert [[1, 2], [3, 4]] == asList([[1, 2], [3, 4]]) // fails!
Another way would be to use the type system to your advantage:
def asList(Collection orig) {
return orig
}
def asList(orig) {
return [orig]
}
assert [1, 2, 3, 4] == asList([1, 2, 3, 4])
assert ["foo"] == asList("foo")
assert [1] == asList(1)
assert [[1, 2], [3, 4]] == asList([[1, 2], [3, 4]]) // works!
Here, we let the type system do all the heavy lifting for us. If we've already got a collection, just return it. Otherwise, turn it into a list. Tricks like this from Java are still available to us in groovy, and we shouldn't completely throw them out when they're the right thing for the problem.

Resources