How can I stop Raku collapsing a list containing a single list? - raku

i've got a function, into which i want to be able to pass a list of lists, like in this artificial example:
sub print_lists(#input) {
.say for #input
}
my #list_of_two_lists = ((1, 2), (3, 4));
print_lists(#list_of_two_lists);
this gives the following output, as expected:
(1, 2)
(3, 4)
but if i do this:
my #list_of_one_list = ((1, 2));
print_lists(#list_of_one_list);
i get this output:
1
2
i.e. it flattens the list containing one list of two elements, into a single list of two elements.
what am i doing wrong? what do i need to do if i want to be able to pass in a list containing a single list?

This behavior is a consequence of two Raku features, both of which are worth knowing.
The first is the Single Argument Rule. It's important enough to be worth reading the docs on, but the key takeaway is that when you pass a single list (as you do in with #list_of_one_list) constructs like for will iterate over each item in the list rather than over the list as a single item. In this case, that means iterating over the two items in the list, 1, and 2.
At this point, you might be thinking "but #list_of_one_list didn't have two items in it – it had one item: the list (1, 2)". But that's because we haven't gotten to the second point to understand: In Raku ( and ) are not what makes something a list. Instead, using the , operator is what constructs a list. This can take a tad bit of getting used to, but it's what allows Raku to treat parentheses as optional in many places that other languages require them.
To see this second point in action, I suggest you check out how .raku prints out your #list_of_lists. Compare:
my #list_of_one_list = ((1, 2));
say #list_of_one_list; # OUTPUT: «[1, 2]»
my #list-of-one-list = (1, 2),;
say #list-of-one-list; # OUTPUT: «[(1, 2)]»
And that's all the info you need to answer your question: simply add a , when you build your list of one list.
I hope that helped :)

Related

List comprehension- Multiple inputs

I am a beginner , trying to understand how list comprehension for multiple input works.
Can someone explain how the below code works?
x,y = [int(x) for x in input("Enter the value ").split()]
print(x,y)
Thanks in advance!
This is actually is not directly related to list comprehensions but instead a concept called "sequence unpacking", which applies to any sequence type (list, tuple, range). What is happening here is that the user input is expected to be two whitespace-separated values. The split call will split the user input on the whitespace, returning a list of size 2. Then, the list comprehension is looping over each element of this split-produced list and converting each one to an int. Thus, the list comprehension will return a list of length 2, and each of its elements will be "unpacked" separately into the x and y variables on the left-hand side of the assignment operator. Here is an excerpt from the Data Structures section of the Python tutorial that explains sequence unpacking:
The statement t = 12345, 54321, 'hello!' is an example of tuple packing: the values 12345, 54321 and 'hello!' are packed together in a tuple. The reverse operation is also possible:
>>> x, y, z = t
This is called, appropriately enough, sequence unpacking and works for
any sequence on the right-hand side. Sequence unpacking requires that
there are as many variables on the left side of the equals sign as
there are elements in the sequence. Note that multiple assignment is
really just a combination of tuple packing and sequence unpacking.
Note that this only works if the user input is of length 2, else the
sequence unpacking will not work and will result in an error.

Using a hash with object keys in Perl 6

I'm trying to make a Hash with non-string keys, in my case arrays or lists.
> my %sum := :{(1, 3, 5) => 9, (2, 4, 6) => 12}
{(1 3 5) => 9, (2 4 6) => 12}
Now, I don't understand the following.
How to retrieve an existing element?
> %sum{(1, 3, 5)}
((Any) (Any) (Any))
> %sum{1, 3, 5}
((Any) (Any) (Any))
How to add a new element?
> %sum{2, 4} = 6
(6 (Any))
Several things are going on here: first of all, if you use (1,2,3) as a key, Rakudo Perl 6 will consider this to be a slice of 3 keys: 1, 2 and 3. Since neither of these exist in the object hash, you get ((Any) (Any) (Any)).
So you need to indicate that you want the list to be seen as single key of which you want the value. You can do this with $(), so %sum{$(1,3,5)}. This however does not give you the intended result. The reason behind that is the following:
> say (1,2,3).WHICH eq (1,2,3).WHICH
False
Object hashes internally key the object to its .WHICH value. At the moment, Lists are not considered value types, so each List has a different .WHICH. Which makes them unfit to be used as keys in object hashes, or in other cases where they are used by default (e.g. .unique and Sets, Bags and Mixes).
I'm actually working on making this the above eq return True before long: this should make it to the 2018.01 compiler release, on which also a Rakudo Star release will be based.
BTW, any time you're using object hashes and integer values, you will probably be better of using Bags. Alas not yet in this case either for the above reason.
You could actually make this work by using augment class List and adding a .WHICH method on that, but I would recommend against that as it will interfere with any future fixes.
Elizabeth's answer is solid, but until that feature is created, I don't see why you can't create a Key class to use as the hash key, which will have an explicit hash function which is based on its values rather than its location in memory. This hash function, used for both placement in the list and equality testing, is .WHICH. This function must return an ObjAt object, which is basically just a string.
class Key does Positional {
has Int #.list handles <elems AT-POS EXISTS-POS ASSIGN-POS BIND-POS push>;
method new(*#list) { self.bless(:#list); }
method WHICH() { ObjAt.new(#!list.join('|')); }
}
my %hsh{Key};
%hsh{Key.new(1, 3)} = 'result';
say %hsh{Key.new(1, 3)}; # output: result
Note that I only allowed the key to contain Int. This is an easy way of being fairly confident no element's string value contains the '|' character, which could make two keys look the same despite having different elements. However, this is not hardened against naughty users--4 but role :: { method Str() { '|' } } is an Int that stringifies to the illegal value. You can make the code stronger if you use .WHICH recursively, but I'll leave that as an exercise.
This Key class is also a little fancier than you strictly need. It would be enough to have a #.list member and define .WHICH. I defined AT-POS and friends so the Key can be indexed, pushed to, and otherwise treated as an Array.

How to avoid having to numerically identify each block in the experiment element in Inquisit?

I often code a study in Inquisit where the study involves running a sequence of blocks. I express the order of the blocks in the form 1=..., 2=..., etc. See the example below.
<expt foostudy>
/blocks=[1=demographics; 2=cogtask; 3=spatialtask]
</expt>
However, it is a hassle when you have many blocks, and you want to add a block in the middle. All the numbers need to be updated.
Is there a way to not have to specify the numbers (e.g., 1, 2, 3) and just let the block order be implied from the sequence they are written?
E.g., Although the following does not work, I'm interested in something like:
<expt foostudy>
/blocks=[demographics; cogtask; spatialtask]
</expt>
It is possible to use the sequence(...) command. I.e., write 1=sequence(...) and place the ordered list of block names between the parentheses separated by commas.
So for the example it would be:
<expt foostudy>
/blocks=[1=sequence(demographics, cogtask, spatialtask)]
</expt>

Expand variable in list in LISP

I wanted the following code to work. How do I make one-level variables expand?
(let* ((data1 10)
(data2 '(data1 20)))
(progn data2))
I expected (10 20) but in the fact I got (data1 20). Also I'd like to get (10 20 (data2)) from the following:
(let* ((data1 10)
(data2 30)
(data3 '(data 10 20 '(data2)))
(progn data3))
The reason that the variable does not expand is that you quoted the whole list using '. ' is not how you create lists in lisp, it's how you quote them (i.e. cause them not to be evaluated).
To create a list containing the contents of data1 and the number 20, just use (list data1 20).
As sepp2k said, the usual way to create lists at runtime is the list function. Quoted lists are list literals, of literal values.
But there's another way to create lists at runtime that looks more similar to quoted lists, the backquote. You could achieve what you want with
`(,data1 20)
Another thing regarding your examples: You don't need the progns there. progn is for sequencing multiple expressions and then return the value of the last of them, if it's just a single value expression, you can just use itself without the wrapping. But even if you had multiple expressions, you wouldn't need to use progn in this case, because let* implicitly puts a progn around the body.

Google Mock: multiple expectations on same function with different parameters

Consider the case where a certain mocked function is expected to be called several times, each time with a different value in a certain parameter. I would like to validate that the function was indeed called once and only once per value in a certain list of values (e.g. 1,2,5).
On the other hand, I would like to refrain from defining a sequence as that would dictate a certain order, which is an implementation detail I would like to keep free.
Is there some kind of matcher, or other solution for this case?
I'm not sure if this influences the solution in any way but I do intend to use WillOnce(Return(x)) with a different x per value in the list above.
By default gMock expectations can be satisfied in any order (precisely for the reason you mention -- so you don't over specify your tests).
In your case, you just want something like:
EXPECT_CALL(foo, DoThis(1));
EXPECT_CALL(foo, DoThis(2));
EXPECT_CALL(foo, DoThis(5));
And something like:
foo.DoThis(5);
foo.DoThis(1);
foo.DoThis(2);
Would satisfy those expectations.
(Aside: If you did want to constrain the order, you should use InSequence: https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md#expecting-ordered-calls-orderedcalls)
If you expect a function, DoThing, to be called with many different parameters, you can use the following pattern:
for (auto const param : {1, 2, 3, 7, -1, 2}){
EXPECT_CALL(foo, DoThing(param));
}
This is particularly helpful if your EXPECT_CALL includes many parameters, of which only one is changing, or if your EXPECT_CALL includes many Actions to be repeated.